summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/net/nfc/nxp,nci.yaml4
-rw-r--r--Documentation/devicetree/bindings/net/qca,ar71xx.yaml1
-rw-r--r--Documentation/networking/bonding.rst4
-rw-r--r--Documentation/networking/device_drivers/ethernet/freescale/dpaa2/mac-phy-support.rst9
-rw-r--r--Documentation/networking/device_drivers/ethernet/mellanox/mlx5.rst82
-rw-r--r--Documentation/networking/devlink/devlink-region.rst13
-rw-r--r--Documentation/networking/devlink/ice.rst13
-rw-r--r--drivers/crypto/marvell/octeontx2/otx2_cpt_devlink.c4
-rw-r--r--drivers/infiniband/hw/mlx5/odp.c3
-rw-r--r--drivers/infiniband/hw/mlx5/umr.c14
-rw-r--r--drivers/net/bonding/bond_main.c2
-rw-r--r--drivers/net/dsa/hirschmann/hellcreek.c5
-rw-r--r--drivers/net/dsa/mv88e6xxx/devlink.c5
-rw-r--r--drivers/net/dsa/sja1105/sja1105_devlink.c12
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c4
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c8
-rw-r--r--drivers/net/ethernet/engleder/tsnep.h8
-rw-r--r--drivers/net/ethernet/engleder/tsnep_ethtool.c165
-rw-r--r--drivers/net/ethernet/engleder/tsnep_hw.h7
-rw-r--r--drivers/net/ethernet/engleder/tsnep_main.c245
-rw-r--r--drivers/net/ethernet/freescale/dpaa2/dpaa2-eth-devlink.c11
-rw-r--r--drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c87
-rw-r--r--drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h11
-rw-r--r--drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c70
-rw-r--r--drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c16
-rw-r--r--drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h10
-rw-r--r--drivers/net/ethernet/freescale/dpaa2/dpaa2-switch-ethtool.c45
-rw-r--r--drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c59
-rw-r--r--drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.h9
-rw-r--r--drivers/net/ethernet/fungible/funeth/funeth_devlink.c7
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_devlink.c5
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_devlink.c5
-rw-r--r--drivers/net/ethernet/intel/ice/ice_devlink.c118
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c7
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c17
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/otx2_devlink.c15
-rw-r--r--drivers/net/ethernet/marvell/prestera/prestera_devlink.c5
-rw-r--r--drivers/net/ethernet/mediatek/mtk_wed_wo.c11
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/cmd.c1
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/devlink.c4
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en.h8
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/params.c4
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/trap.c10
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c17
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h5
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c12
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_main.c9
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_rep.c10
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_rx.c21
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_tc.h4
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/eswitch.h5
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c35
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads_termtbl.c32
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lib/aso.c3
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/main.c3
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/uar.c1
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/core.c5
-rw-r--r--drivers/net/ethernet/microchip/vcap/vcap_api.c7
-rw-r--r--drivers/net/ethernet/microchip/vcap/vcap_api.h1
-rw-r--r--drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c15
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_devlink.c4
-rw-r--r--drivers/net/ethernet/pensando/ionic/ionic_devlink.c4
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_devlink.c4
-rw-r--r--drivers/net/ethernet/realtek/r8169_main.c7
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/Kconfig9
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/Makefile1
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-tegra.c388
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_main.c7
-rw-r--r--drivers/net/netdevsim/dev.c3
-rw-r--r--drivers/net/phy/phy_device.c12
-rw-r--r--drivers/net/thunderbolt.c18
-rw-r--r--drivers/net/wireless/ath/ath10k/Kconfig1
-rw-r--r--drivers/net/wireless/ath/ath10k/htt_rx.c25
-rw-r--r--drivers/net/wireless/ath/ath10k/pci.c20
-rw-r--r--drivers/net/wireless/ath/ath10k/qmi.c37
-rw-r--r--drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.c126
-rw-r--r--drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.h102
-rw-r--r--drivers/net/wireless/ath/ath11k/core.c6
-rw-r--r--drivers/net/wireless/ath/ath11k/hw.h1
-rw-r--r--drivers/net/wireless/ath/ath11k/mac.c7
-rw-r--r--drivers/net/wireless/ath/ath11k/mac.h2
-rw-r--r--drivers/net/wireless/ath/ath11k/qmi.c75
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_mci.c3
-rw-r--r--drivers/net/wireless/ath/ath9k/xmit.c2
-rw-r--r--drivers/net/wireless/ath/carl9170/fwcmd.h4
-rw-r--r--drivers/net/wireless/broadcom/b43/main.c10
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c17
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c7
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h4
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c6
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/mac.h6
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/phy-ctxt.h33
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/rs.h10
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/rs.c2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-drv.c3
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.h3
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-modparams.h1
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c235
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-prph.h1
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-trans.h1
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/fw.c29
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c189
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/mvm.h2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/ops.c13
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c54
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c125
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/tx.c12
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/trans.c85
-rw-r--r--drivers/net/wireless/intersil/p54/eeprom.h4
-rw-r--r--drivers/net/wireless/mediatek/mt76/Kconfig1
-rw-r--r--drivers/net/wireless/mediatek/mt76/Makefile1
-rw-r--r--drivers/net/wireless/mediatek/mt76/debugfs.c19
-rw-r--r--drivers/net/wireless/mediatek/mt76/dma.c244
-rw-r--r--drivers/net/wireless/mediatek/mt76/dma.h8
-rw-r--r--drivers/net/wireless/mediatek/mt76/mac80211.c27
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76.h50
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7603/debugfs.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7603/dma.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7603/mac.c6
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7603/mt7603.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/debugfs.c6
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/mac.c34
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/main.c7
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/mcu.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h6
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/regs.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76_connac.h16
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c17
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c214
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h99
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.c28
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x0/init.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x0/phy.c13
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02.h16
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_debugfs.c19
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_eeprom.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_mac.c6
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_phy.c22
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_phy.h6
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_txrx.c14
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.c16
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/init.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/phy.c6
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/Kconfig1
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/Makefile3
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/coredump.c410
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/coredump.h136
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c307
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/dma.c207
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c66
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/eeprom.h5
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/init.c135
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/mac.c635
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/main.c142
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/mcu.c495
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/mcu.h60
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/mmio.c414
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h65
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/pci.c106
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/regs.h88
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/soc.c21
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/testmode.c71
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/debugfs.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/init.c91
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/mac.c56
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/main.c233
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/mcu.c161
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h74
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/pci.c59
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/sdio.c31
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/usb.c22
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/Kconfig12
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/Makefile6
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c851
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/dma.c360
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c229
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/eeprom.h75
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/init.c823
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/mac.c2498
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/mac.h398
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/main.c1334
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/mcu.c3607
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/mcu.h669
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/mmio.c386
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h523
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/pci.c222
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/regs.h542
-rw-r--r--drivers/net/wireless/mediatek/mt76/sdio.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/tx.c30
-rw-r--r--drivers/net/wireless/mediatek/mt76/usb.c13
-rw-r--r--drivers/net/wireless/mediatek/mt76/util.h6
-rw-r--r--drivers/net/wireless/purelifi/plfxlc/usb.c1
-rw-r--r--drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c2
-rw-r--r--drivers/net/wireless/realtek/rtw88/fw.c18
-rw-r--r--drivers/net/wireless/realtek/rtw88/fw.h11
-rw-r--r--drivers/net/wireless/realtek/rtw88/mac.c18
-rw-r--r--drivers/net/wireless/realtek/rtw89/chan.c40
-rw-r--r--drivers/net/wireless/realtek/rtw89/core.c73
-rw-r--r--drivers/net/wireless/realtek/rtw89/core.h83
-rw-r--r--drivers/net/wireless/realtek/rtw89/debug.h1
-rw-r--r--drivers/net/wireless/realtek/rtw89/fw.c386
-rw-r--r--drivers/net/wireless/realtek/rtw89/fw.h451
-rw-r--r--drivers/net/wireless/realtek/rtw89/mac.c188
-rw-r--r--drivers/net/wireless/realtek/rtw89/mac.h35
-rw-r--r--drivers/net/wireless/realtek/rtw89/mac80211.c12
-rw-r--r--drivers/net/wireless/realtek/rtw89/pci.c2
-rw-r--r--drivers/net/wireless/realtek/rtw89/phy.c129
-rw-r--r--drivers/net/wireless/realtek/rtw89/phy.h5
-rw-r--r--drivers/net/wireless/realtek/rtw89/reg.h2
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852a.c2
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852b.c39
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852c.c6
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852c_rfk.c20
-rw-r--r--drivers/net/wireless/realtek/rtw89/txrx.h4
-rw-r--r--drivers/ptp/ptp_ocp.c4
-rw-r--r--include/linux/ieee80211.h28
-rw-r--r--include/linux/jump_label.h21
-rw-r--r--include/linux/mlx5/device.h7
-rw-r--r--include/linux/mlx5/driver.h2
-rw-r--r--include/linux/netdevice.h1
-rw-r--r--include/linux/phy.h4
-rw-r--r--include/linux/stmmac.h1
-rw-r--r--include/net/cfg80211.h2
-rw-r--r--include/net/devlink.h18
-rw-r--r--include/net/fq_impl.h16
-rw-r--r--include/net/mac80211.h5
-rw-r--r--include/net/sctp/stream_sched.h2
-rw-r--r--include/net/tcp.h10
-rw-r--r--include/uapi/linux/devlink.h2
-rw-r--r--include/uapi/linux/mptcp.h9
-rw-r--r--kernel/jump_label.c58
-rw-r--r--net/core/dev.c16
-rw-r--r--net/core/devlink.c231
-rw-r--r--net/hsr/hsr_debugfs.c40
-rw-r--r--net/hsr/hsr_device.c31
-rw-r--r--net/hsr/hsr_forward.c14
-rw-r--r--net/hsr/hsr_framereg.c265
-rw-r--r--net/hsr/hsr_framereg.h17
-rw-r--r--net/hsr/hsr_main.h15
-rw-r--r--net/hsr/hsr_netlink.c4
-rw-r--r--net/ipv4/tcp.c5
-rw-r--r--net/ipv4/tcp_ipv4.c96
-rw-r--r--net/ipv4/tcp_minisocks.c61
-rw-r--r--net/ipv4/tcp_output.c4
-rw-r--r--net/ipv6/tcp_ipv6.c21
-rw-r--r--net/mac80211/cfg.c2
-rw-r--r--net/mac80211/iface.c5
-rw-r--r--net/mac80211/mlme.c2
-rw-r--r--net/mac80211/rx.c38
-rw-r--r--net/mac80211/tx.c299
-rw-r--r--net/mptcp/pm_netlink.c57
-rw-r--r--net/mptcp/protocol.c3
-rw-r--r--net/mptcp/protocol.h2
-rw-r--r--net/sctp/stream_sched.c38
-rw-r--r--net/sctp/stream_sched_prio.c27
-rw-r--r--net/sctp/stream_sched_rr.c6
-rw-r--r--net/wireless/nl80211.c3
-rw-r--r--net/wireless/reg.c4
-rw-r--r--net/wireless/scan.c44
-rw-r--r--tools/testing/selftests/Makefile1
-rw-r--r--tools/testing/selftests/net/hsr/Makefile7
-rw-r--r--tools/testing/selftests/net/hsr/config4
-rwxr-xr-xtools/testing/selftests/net/hsr/hsr_ping.sh256
-rwxr-xr-xtools/testing/selftests/net/mptcp/diag.sh1
-rwxr-xr-xtools/testing/selftests/net/mptcp/mptcp_connect.sh6
-rwxr-xr-xtools/testing/selftests/net/mptcp/mptcp_join.sh118
-rwxr-xr-xtools/testing/selftests/net/mptcp/mptcp_sockopt.sh69
-rwxr-xr-xtools/testing/selftests/net/mptcp/simult_flows.sh4
-rwxr-xr-xtools/testing/selftests/net/mptcp/userspace_pm.sh298
271 files changed, 21698 insertions, 2785 deletions
diff --git a/Documentation/devicetree/bindings/net/nfc/nxp,nci.yaml b/Documentation/devicetree/bindings/net/nfc/nxp,nci.yaml
index b2558421268a..6924aff0b2c5 100644
--- a/Documentation/devicetree/bindings/net/nfc/nxp,nci.yaml
+++ b/Documentation/devicetree/bindings/net/nfc/nxp,nci.yaml
@@ -14,7 +14,9 @@ properties:
oneOf:
- const: nxp,nxp-nci-i2c
- items:
- - const: nxp,pn547
+ - enum:
+ - nxp,nq310
+ - nxp,pn547
- const: nxp,nxp-nci-i2c
enable-gpios:
diff --git a/Documentation/devicetree/bindings/net/qca,ar71xx.yaml b/Documentation/devicetree/bindings/net/qca,ar71xx.yaml
index 1ebf9e8c8a1d..89f94b31b546 100644
--- a/Documentation/devicetree/bindings/net/qca,ar71xx.yaml
+++ b/Documentation/devicetree/bindings/net/qca,ar71xx.yaml
@@ -123,7 +123,6 @@ examples:
switch_port0: port@0 {
reg = <0x0>;
- label = "cpu";
ethernet = <&eth1>;
phy-mode = "gmii";
diff --git a/Documentation/networking/bonding.rst b/Documentation/networking/bonding.rst
index 96cd7a26f3d9..adc4bf4f3c50 100644
--- a/Documentation/networking/bonding.rst
+++ b/Documentation/networking/bonding.rst
@@ -566,7 +566,8 @@ miimon
link monitoring. A value of 100 is a good starting point.
The use_carrier option, below, affects how the link state is
determined. See the High Availability section for additional
- information. The default value is 0.
+ information. The default value is 100 if arp_interval is not
+ set.
min_links
@@ -956,6 +957,7 @@ xmit_hash_policy
hash = hash XOR source IP XOR destination IP
hash = hash XOR (hash RSHIFT 16)
hash = hash XOR (hash RSHIFT 8)
+ hash = hash RSHIFT 1
And then hash is reduced modulo slave count.
If the protocol is IPv6 then the source and destination
diff --git a/Documentation/networking/device_drivers/ethernet/freescale/dpaa2/mac-phy-support.rst b/Documentation/networking/device_drivers/ethernet/freescale/dpaa2/mac-phy-support.rst
index 51e6624fb774..1d2f55feca24 100644
--- a/Documentation/networking/device_drivers/ethernet/freescale/dpaa2/mac-phy-support.rst
+++ b/Documentation/networking/device_drivers/ethernet/freescale/dpaa2/mac-phy-support.rst
@@ -181,10 +181,13 @@ when necessary using the below listed API::
- int dpaa2_mac_connect(struct dpaa2_mac *mac);
- void dpaa2_mac_disconnect(struct dpaa2_mac *mac);
-A phylink integration is necessary only when the partner DPMAC is not of TYPE_FIXED.
-One can check for this condition using the below API::
+A phylink integration is necessary only when the partner DPMAC is not of
+``TYPE_FIXED``. This means it is either of ``TYPE_PHY``, or of
+``TYPE_BACKPLANE`` (the difference being the two that in the ``TYPE_BACKPLANE``
+mode, the MC firmware does not access the PCS registers). One can check for
+this condition using the following helper::
- - bool dpaa2_mac_is_type_fixed(struct fsl_mc_device *dpmac_dev,struct fsl_mc_io *mc_io);
+ - static inline bool dpaa2_mac_is_type_phy(struct dpaa2_mac *mac);
Before connection to a MAC, the caller must allocate and populate the
dpaa2_mac structure with the associated net_device, a pointer to the MC portal
diff --git a/Documentation/networking/device_drivers/ethernet/mellanox/mlx5.rst b/Documentation/networking/device_drivers/ethernet/mellanox/mlx5.rst
index 5edf50d7dbd5..e8fa7ac9e6b1 100644
--- a/Documentation/networking/device_drivers/ethernet/mellanox/mlx5.rst
+++ b/Documentation/networking/device_drivers/ethernet/mellanox/mlx5.rst
@@ -25,7 +25,7 @@ Enabling the driver and kconfig options
| at build time via kernel Kconfig flags.
| Basic features, ethernet net device rx/tx offloads and XDP, are available with the most basic flags
| CONFIG_MLX5_CORE=y/m and CONFIG_MLX5_CORE_EN=y.
-| For the list of advanced features please see below.
+| For the list of advanced features, please see below.
**CONFIG_MLX5_CORE=(y/m/n)** (module mlx5_core.ko)
@@ -89,11 +89,11 @@ Enabling the driver and kconfig options
**CONFIG_MLX5_EN_IPSEC=(y/n)**
-| Enables `IPSec XFRM cryptography-offload accelaration <http://www.mellanox.com/related-docs/prod_software/Mellanox_Innova_IPsec_Ethernet_Adapter_Card_User_Manual.pdf>`_.
+| Enables `IPSec XFRM cryptography-offload acceleration <http://www.mellanox.com/related-docs/prod_software/Mellanox_Innova_IPsec_Ethernet_Adapter_Card_User_Manual.pdf>`_.
**CONFIG_MLX5_EN_TLS=(y/n)**
-| TLS cryptography-offload accelaration.
+| TLS cryptography-offload acceleration.
**CONFIG_MLX5_INFINIBAND=(y/n/m)** (module mlx5_ib.ko)
@@ -139,14 +139,14 @@ flow_steering_mode: Device flow steering mode
The flow steering mode parameter controls the flow steering mode of the driver.
Two modes are supported:
1. 'dmfs' - Device managed flow steering.
-2. 'smfs - Software/Driver managed flow steering.
+2. 'smfs' - Software/Driver managed flow steering.
In DMFS mode, the HW steering entities are created and managed through the
Firmware.
In SMFS mode, the HW steering entities are created and managed though by
-the driver directly into Hardware without firmware intervention.
+the driver directly into hardware without firmware intervention.
-SMFS mode is faster and provides better rule inserstion rate compared to default DMFS mode.
+SMFS mode is faster and provides better rule insertion rate compared to default DMFS mode.
User command examples:
@@ -165,9 +165,9 @@ User command examples:
enable_roce: RoCE enablement state
----------------------------------
RoCE enablement state controls driver support for RoCE traffic.
-When RoCE is disabled, there is no gid table, only raw ethernet QPs are supported and traffic on the well known UDP RoCE port is handled as raw ethernet traffic.
+When RoCE is disabled, there is no gid table, only raw ethernet QPs are supported and traffic on the well-known UDP RoCE port is handled as raw ethernet traffic.
-To change RoCE enablement state a user must change the driverinit cmode value and run devlink reload.
+To change RoCE enablement state, a user must change the driverinit cmode value and run devlink reload.
User command examples:
@@ -186,7 +186,7 @@ User command examples:
esw_port_metadata: Eswitch port metadata state
----------------------------------------------
-When applicable, disabling Eswitch metadata can increase packet rate
+When applicable, disabling eswitch metadata can increase packet rate
up to 20% depending on the use case and packet sizes.
Eswitch port metadata state controls whether to internally tag packets with
@@ -253,26 +253,26 @@ mlx5 subfunction
================
mlx5 supports subfunction management using devlink port (see :ref:`Documentation/networking/devlink/devlink-port.rst <devlink_port>`) interface.
-A Subfunction has its own function capabilities and its own resources. This
+A subfunction has its own function capabilities and its own resources. This
means a subfunction has its own dedicated queues (txq, rxq, cq, eq). These
queues are neither shared nor stolen from the parent PCI function.
-When a subfunction is RDMA capable, it has its own QP1, GID table and rdma
+When a subfunction is RDMA capable, it has its own QP1, GID table, and RDMA
resources neither shared nor stolen from the parent PCI function.
A subfunction has a dedicated window in PCI BAR space that is not shared
-with ther other subfunctions or the parent PCI function. This ensures that all
-devices (netdev, rdma, vdpa etc.) of the subfunction accesses only assigned
+with the other subfunctions or the parent PCI function. This ensures that all
+devices (netdev, rdma, vdpa, etc.) of the subfunction accesses only assigned
PCI BAR space.
-A Subfunction supports eswitch representation through which it supports tc
+A subfunction supports eswitch representation through which it supports tc
offloads. The user configures eswitch to send/receive packets from/to
the subfunction port.
Subfunctions share PCI level resources such as PCI MSI-X IRQs with
other subfunctions and/or with its parent PCI function.
-Example mlx5 software, system and device view::
+Example mlx5 software, system, and device view::
_______
| admin |
@@ -310,7 +310,7 @@ Example mlx5 software, system and device view::
| (device add/del)
_____|____ ____|________
| | | subfunction |
- | PCI NIC |---- activate/deactive events---->| host driver |
+ | PCI NIC |--- activate/deactivate events--->| host driver |
|__________| | (mlx5_core) |
|_____________|
@@ -320,7 +320,7 @@ Subfunction is created using devlink port interface.
$ devlink dev eswitch set pci/0000:06:00.0 mode switchdev
-- Add a devlink port of subfunction flaovur::
+- Add a devlink port of subfunction flavour::
$ devlink port add pci/0000:06:00.0 flavour pcisf pfnum 0 sfnum 88
pci/0000:06:00.0/32768: type eth netdev eth6 flavour pcisf controller 0 pfnum 0 sfnum 88 external false splittable false
@@ -379,18 +379,18 @@ device created for the PCI VF/SF.
function:
hw_addr 00:00:00:00:00:00
-- Set the MAC address of the VF identified by its unique devlink port index::
+- Set the MAC address of the SF identified by its unique devlink port index::
$ devlink port function set pci/0000:06:00.0/32768 hw_addr 00:00:00:00:88:88
$ devlink port show pci/0000:06:00.0/32768
- pci/0000:06:00.0/32768: type eth netdev enp6s0pf0sf88 flavour pcivf pfnum 0 sfnum 88
+ pci/0000:06:00.0/32768: type eth netdev enp6s0pf0sf88 flavour pcisf pfnum 0 sfnum 88
function:
hw_addr 00:00:00:00:88:88
SF state setup
--------------
-To use the SF, the user must active the SF using the SF function state
+To use the SF, the user must activate the SF using the SF function state
attribute.
- Get the state of the SF identified by its unique devlink port index::
@@ -447,7 +447,7 @@ for it.
Additionally, the SF port also gets the event when the driver attaches to the
auxiliary device of the subfunction. This results in changing the operational
-state of the function. This provides visiblity to the user to decide when is it
+state of the function. This provides visibility to the user to decide when is it
safe to delete the SF port for graceful termination of the subfunction.
- Show the SF port operational state::
@@ -464,14 +464,14 @@ tx reporter
-----------
The tx reporter is responsible for reporting and recovering of the following two error scenarios:
-- TX timeout
+- tx timeout
Report on kernel tx timeout detection.
Recover by searching lost interrupts.
-- TX error completion
+- tx error completion
Report on error tx completion.
- Recover by flushing the TX queue and reset it.
+ Recover by flushing the tx queue and reset it.
-TX reporter also support on demand diagnose callback, on which it provides
+tx reporter also support on demand diagnose callback, on which it provides
real time information of its send queues status.
User commands examples:
@@ -491,32 +491,32 @@ rx reporter
-----------
The rx reporter is responsible for reporting and recovering of the following two error scenarios:
-- RX queues initialization (population) timeout
- RX queues descriptors population on ring initialization is done in
- napi context via triggering an irq, in case of a failure to get
- the minimum amount of descriptors, a timeout would occur and it
- could be recoverable by polling the EQ (Event Queue).
-- RX completions with errors (reported by HW on interrupt context)
+- rx queues' initialization (population) timeout
+ Population of rx queues' descriptors on ring initialization is done
+ in napi context via triggering an irq. In case of a failure to get
+ the minimum amount of descriptors, a timeout would occur, and
+ descriptors could be recovered by polling the EQ (Event Queue).
+- rx completions with errors (reported by HW on interrupt context)
Report on rx completion error.
Recover (if needed) by flushing the related queue and reset it.
-RX reporter also supports on demand diagnose callback, on which it
-provides real time information of its receive queues status.
+rx reporter also supports on demand diagnose callback, on which it
+provides real time information of its receive queues' status.
-- Diagnose rx queues status, and corresponding completion queue::
+- Diagnose rx queues' status and corresponding completion queue::
$ devlink health diagnose pci/0000:82:00.0 reporter rx
-NOTE: This command has valid output only when interface is up, otherwise the command has empty output.
+NOTE: This command has valid output only when interface is up. Otherwise, the command has empty output.
- Show number of rx errors indicated, number of recover flows ended successfully,
- is autorecover enabled and graceful period from last recover::
+ is autorecover enabled, and graceful period from last recover::
$ devlink health show pci/0000:82:00.0 reporter rx
fw reporter
-----------
-The fw reporter implements diagnose and dump callbacks.
+The fw reporter implements `diagnose` and `dump` callbacks.
It follows symptoms of fw error such as fw syndrome by triggering
fw core dump and storing it into the dump buffer.
The fw reporter diagnose command can be triggered any time by the user to check
@@ -537,7 +537,7 @@ running it on other PF or any VF will return "Operation not permitted".
fw fatal reporter
-----------------
-The fw fatal reporter implements dump and recover callbacks.
+The fw fatal reporter implements `dump` and `recover` callbacks.
It follows fatal errors indications by CR-space dump and recover flow.
The CR-space dump uses vsc interface which is valid even if the FW command
interface is not functional, which is the case in most FW fatal errors.
@@ -552,7 +552,7 @@ User commands examples:
$ devlink health recover pci/0000:82:00.0 reporter fw_fatal
-- Read FW CR-space dump if already strored or trigger new one::
+- Read FW CR-space dump if already stored or trigger new one::
$ devlink health dump show pci/0000:82:00.1 reporter fw_fatal
@@ -561,10 +561,10 @@ NOTE: This command can run only on PF.
mlx5 tracepoints
================
-mlx5 driver provides internal trace points for tracking and debugging using
+mlx5 driver provides internal tracepoints for tracking and debugging using
kernel tracepoints interfaces (refer to Documentation/trace/ftrace.rst).
-For the list of support mlx5 events check /sys/kernel/debug/tracing/events/mlx5/
+For the list of support mlx5 events, check `/sys/kernel/debug/tracing/events/mlx5/`.
tc and eswitch offloads tracepoints:
diff --git a/Documentation/networking/devlink/devlink-region.rst b/Documentation/networking/devlink/devlink-region.rst
index f06dca9a1eb6..9232cd7da301 100644
--- a/Documentation/networking/devlink/devlink-region.rst
+++ b/Documentation/networking/devlink/devlink-region.rst
@@ -31,6 +31,15 @@ in its ``devlink_region_ops`` structure. If snapshot id is not set in
the ``DEVLINK_CMD_REGION_NEW`` request kernel will allocate one and send
the snapshot information to user space.
+Regions may optionally allow directly reading from their contents without a
+snapshot. Direct read requests are not atomic. In particular a read request
+of size 256 bytes or larger will be split into multiple chunks. If atomic
+access is required, use a snapshot. A driver wishing to enable this for a
+region should implement the ``.read`` callback in the ``devlink_region_ops``
+structure. User space can request a direct read by using the
+``DEVLINK_ATTR_REGION_DIRECT`` attribute instead of specifying a snapshot
+id.
+
example usage
-------------
@@ -65,6 +74,10 @@ example usage
$ devlink region read pci/0000:00:05.0/fw-health snapshot 1 address 0 length 16
0000000000000000 0014 95dc 0014 9514 0035 1670 0034 db30
+ # Read from the region without a snapshot
+ $ devlink region read pci/0000:00:05.0/fw-health address 16 length 16
+ 0000000000000010 0000 0000 ffff ff04 0029 8c00 0028 8cc8
+
As regions are likely very device or driver specific, no generic regions are
defined. See the driver-specific documentation files for information on the
specific regions a driver supports.
diff --git a/Documentation/networking/devlink/ice.rst b/Documentation/networking/devlink/ice.rst
index 890062da7820..625efb3777d5 100644
--- a/Documentation/networking/devlink/ice.rst
+++ b/Documentation/networking/devlink/ice.rst
@@ -189,12 +189,21 @@ device data.
* - ``nvm-flash``
- The contents of the entire flash chip, sometimes referred to as
the device's Non Volatile Memory.
+ * - ``shadow-ram``
+ - The contents of the Shadow RAM, which is loaded from the beginning
+ of the flash. Although the contents are primarily from the flash,
+ this area also contains data generated during device boot which is
+ not stored in flash.
* - ``device-caps``
- The contents of the device firmware's capabilities buffer. Useful to
determine the current state and configuration of the device.
-Users can request an immediate capture of a snapshot via the
-``DEVLINK_CMD_REGION_NEW``
+Both the ``nvm-flash`` and ``shadow-ram`` regions can be accessed without a
+snapshot. The ``device-caps`` region requires a snapshot as the contents are
+sent by firmware and can't be split into separate reads.
+
+Users can request an immediate capture of a snapshot for all three regions
+via the ``DEVLINK_CMD_REGION_NEW`` command.
.. code:: shell
diff --git a/drivers/crypto/marvell/octeontx2/otx2_cpt_devlink.c b/drivers/crypto/marvell/octeontx2/otx2_cpt_devlink.c
index 7503f6b18ac5..a2aba0b0d68a 100644
--- a/drivers/crypto/marvell/octeontx2/otx2_cpt_devlink.c
+++ b/drivers/crypto/marvell/octeontx2/otx2_cpt_devlink.c
@@ -76,10 +76,6 @@ static int otx2_cpt_devlink_info_get(struct devlink *dl,
struct otx2_cptpf_dev *cptpf = cpt_dl->cptpf;
int err;
- err = devlink_info_driver_name_put(req, "rvu_cptpf");
- if (err)
- return err;
-
err = otx2_cpt_dl_info_firmware_version_put(req, cptpf->eng_grps.grp,
"fw.ae", OTX2_CPT_AE_TYPES);
if (err)
diff --git a/drivers/infiniband/hw/mlx5/odp.c b/drivers/infiniband/hw/mlx5/odp.c
index bc97958818bb..e6e021af6aa9 100644
--- a/drivers/infiniband/hw/mlx5/odp.c
+++ b/drivers/infiniband/hw/mlx5/odp.c
@@ -230,8 +230,7 @@ static bool mlx5_ib_invalidate_range(struct mmu_interval_notifier *mni,
struct ib_umem_odp *umem_odp =
container_of(mni, struct ib_umem_odp, notifier);
struct mlx5_ib_mr *mr;
- const u64 umr_block_mask = (MLX5_UMR_MTT_ALIGNMENT /
- sizeof(struct mlx5_mtt)) - 1;
+ const u64 umr_block_mask = MLX5_UMR_MTT_NUM_ENTRIES_ALIGNMENT - 1;
u64 idx = 0, blk_start_idx = 0;
u64 invalidations = 0;
unsigned long start;
diff --git a/drivers/infiniband/hw/mlx5/umr.c b/drivers/infiniband/hw/mlx5/umr.c
index d5105b5c9979..029e9536ec28 100644
--- a/drivers/infiniband/hw/mlx5/umr.c
+++ b/drivers/infiniband/hw/mlx5/umr.c
@@ -418,7 +418,7 @@ int mlx5r_umr_rereg_pd_access(struct mlx5_ib_mr *mr, struct ib_pd *pd,
}
#define MLX5_MAX_UMR_CHUNK \
- ((1 << (MLX5_MAX_UMR_SHIFT + 4)) - MLX5_UMR_MTT_ALIGNMENT)
+ ((1 << (MLX5_MAX_UMR_SHIFT + 4)) - MLX5_UMR_FLEX_ALIGNMENT)
#define MLX5_SPARE_UMR_CHUNK 0x10000
/*
@@ -428,11 +428,11 @@ int mlx5r_umr_rereg_pd_access(struct mlx5_ib_mr *mr, struct ib_pd *pd,
*/
static void *mlx5r_umr_alloc_xlt(size_t *nents, size_t ent_size, gfp_t gfp_mask)
{
- const size_t xlt_chunk_align = MLX5_UMR_MTT_ALIGNMENT / ent_size;
+ const size_t xlt_chunk_align = MLX5_UMR_FLEX_ALIGNMENT / ent_size;
size_t size;
void *res = NULL;
- static_assert(PAGE_SIZE % MLX5_UMR_MTT_ALIGNMENT == 0);
+ static_assert(PAGE_SIZE % MLX5_UMR_FLEX_ALIGNMENT == 0);
/*
* MLX5_IB_UPD_XLT_ATOMIC doesn't signal an atomic context just that the
@@ -666,7 +666,7 @@ int mlx5r_umr_update_mr_pas(struct mlx5_ib_mr *mr, unsigned int flags)
}
final_size = (void *)cur_mtt - (void *)mtt;
- sg.length = ALIGN(final_size, MLX5_UMR_MTT_ALIGNMENT);
+ sg.length = ALIGN(final_size, MLX5_UMR_FLEX_ALIGNMENT);
memset(cur_mtt, 0, sg.length - final_size);
mlx5r_umr_final_update_xlt(dev, &wqe, mr, &sg, flags);
@@ -690,7 +690,7 @@ int mlx5r_umr_update_xlt(struct mlx5_ib_mr *mr, u64 idx, int npages,
int desc_size = (flags & MLX5_IB_UPD_XLT_INDIRECT)
? sizeof(struct mlx5_klm)
: sizeof(struct mlx5_mtt);
- const int page_align = MLX5_UMR_MTT_ALIGNMENT / desc_size;
+ const int page_align = MLX5_UMR_FLEX_ALIGNMENT / desc_size;
struct mlx5_ib_dev *dev = mr_to_mdev(mr);
struct device *ddev = &dev->mdev->pdev->dev;
const int page_mask = page_align - 1;
@@ -711,7 +711,7 @@ int mlx5r_umr_update_xlt(struct mlx5_ib_mr *mr, u64 idx, int npages,
if (WARN_ON(!mr->umem->is_odp))
return -EINVAL;
- /* UMR copies MTTs in units of MLX5_UMR_MTT_ALIGNMENT bytes,
+ /* UMR copies MTTs in units of MLX5_UMR_FLEX_ALIGNMENT bytes,
* so we need to align the offset and length accordingly
*/
if (idx & page_mask) {
@@ -748,7 +748,7 @@ int mlx5r_umr_update_xlt(struct mlx5_ib_mr *mr, u64 idx, int npages,
mlx5_odp_populate_xlt(xlt, idx, npages, mr, flags);
dma_sync_single_for_device(ddev, sg.addr, sg.length,
DMA_TO_DEVICE);
- sg.length = ALIGN(size_to_map, MLX5_UMR_MTT_ALIGNMENT);
+ sg.length = ALIGN(size_to_map, MLX5_UMR_FLEX_ALIGNMENT);
if (pages_mapped + pages_iter >= pages_to_map)
mlx5r_umr_final_update_xlt(dev, &wqe, mr, &sg, flags);
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index c87481033995..e01bb0412f1c 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -2524,10 +2524,10 @@ static int bond_slave_info_query(struct net_device *bond_dev, struct ifslave *in
/* called with rcu_read_lock() */
static int bond_miimon_inspect(struct bonding *bond)
{
+ bool ignore_updelay = false;
int link_state, commit = 0;
struct list_head *iter;
struct slave *slave;
- bool ignore_updelay;
if (BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP) {
ignore_updelay = !rcu_dereference(bond->curr_active_slave);
diff --git a/drivers/net/dsa/hirschmann/hellcreek.c b/drivers/net/dsa/hirschmann/hellcreek.c
index 951f7935c872..595a548bb0a8 100644
--- a/drivers/net/dsa/hirschmann/hellcreek.c
+++ b/drivers/net/dsa/hirschmann/hellcreek.c
@@ -1176,11 +1176,6 @@ static int hellcreek_devlink_info_get(struct dsa_switch *ds,
struct netlink_ext_ack *extack)
{
struct hellcreek *hellcreek = ds->priv;
- int ret;
-
- ret = devlink_info_driver_name_put(req, "hellcreek");
- if (ret)
- return ret;
return devlink_info_version_fixed_put(req,
DEVLINK_INFO_VERSION_GENERIC_ASIC_ID,
diff --git a/drivers/net/dsa/mv88e6xxx/devlink.c b/drivers/net/dsa/mv88e6xxx/devlink.c
index 1266eabee086..a08dab75e0c0 100644
--- a/drivers/net/dsa/mv88e6xxx/devlink.c
+++ b/drivers/net/dsa/mv88e6xxx/devlink.c
@@ -821,11 +821,6 @@ int mv88e6xxx_devlink_info_get(struct dsa_switch *ds,
struct netlink_ext_ack *extack)
{
struct mv88e6xxx_chip *chip = ds->priv;
- int err;
-
- err = devlink_info_driver_name_put(req, "mv88e6xxx");
- if (err)
- return err;
return devlink_info_version_fixed_put(req,
DEVLINK_INFO_VERSION_GENERIC_ASIC_ID,
diff --git a/drivers/net/dsa/sja1105/sja1105_devlink.c b/drivers/net/dsa/sja1105/sja1105_devlink.c
index 10c6fea1227f..da532614f34a 100644
--- a/drivers/net/dsa/sja1105/sja1105_devlink.c
+++ b/drivers/net/dsa/sja1105/sja1105_devlink.c
@@ -120,16 +120,10 @@ int sja1105_devlink_info_get(struct dsa_switch *ds,
struct netlink_ext_ack *extack)
{
struct sja1105_private *priv = ds->priv;
- int rc;
-
- rc = devlink_info_driver_name_put(req, "sja1105");
- if (rc)
- return rc;
- rc = devlink_info_version_fixed_put(req,
- DEVLINK_INFO_VERSION_GENERIC_ASIC_ID,
- priv->info->name);
- return rc;
+ return devlink_info_version_fixed_put(req,
+ DEVLINK_INFO_VERSION_GENERIC_ASIC_ID,
+ priv->info->name);
}
int sja1105_devlink_setup(struct dsa_switch *ds)
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c
index 8a6f788f6294..26913dc816d3 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c
@@ -892,10 +892,6 @@ static int bnxt_dl_info_get(struct devlink *dl, struct devlink_info_req *req,
u32 ver = 0;
int rc;
- rc = devlink_info_driver_name_put(req, DRV_MODULE_NAME);
- if (rc)
- return rc;
-
if (BNXT_PF(bp) && (bp->flags & BNXT_FLAG_DSN_VALID)) {
sprintf(buf, "%02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X",
bp->dsn[7], bp->dsn[6], bp->dsn[5], bp->dsn[4],
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
index c2f663770a7f..cbf17fcfb7ab 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
@@ -2007,6 +2007,14 @@ static void bnxt_get_fec_stats(struct net_device *dev,
rx = bp->rx_port_stats_ext.sw_stats;
fec_stats->corrected_bits.total =
*(rx + BNXT_RX_STATS_EXT_OFFSET(rx_corrected_bits));
+
+ if (bp->fw_rx_stats_ext_size <= BNXT_RX_STATS_EXT_NUM_LEGACY)
+ return;
+
+ fec_stats->corrected_blocks.total =
+ *(rx + BNXT_RX_STATS_EXT_OFFSET(rx_fec_corrected_blocks));
+ fec_stats->uncorrectable_blocks.total =
+ *(rx + BNXT_RX_STATS_EXT_OFFSET(rx_fec_uncorrectable_blocks));
}
static u32 bnxt_ethtool_forced_fec_to_fw(struct bnxt_link_info *link_info,
diff --git a/drivers/net/ethernet/engleder/tsnep.h b/drivers/net/ethernet/engleder/tsnep.h
index 09a723b827c7..f93ba48bac3f 100644
--- a/drivers/net/ethernet/engleder/tsnep.h
+++ b/drivers/net/ethernet/engleder/tsnep.h
@@ -18,6 +18,8 @@
#define TSNEP "tsnep"
#define TSNEP_RING_SIZE 256
+#define TSNEP_RING_RX_REFILL 16
+#define TSNEP_RING_RX_REUSE (TSNEP_RING_SIZE - TSNEP_RING_SIZE / 4)
#define TSNEP_RING_ENTRIES_PER_PAGE (PAGE_SIZE / TSNEP_DESC_SIZE)
#define TSNEP_RING_PAGE_COUNT (TSNEP_RING_SIZE / TSNEP_RING_ENTRIES_PER_PAGE)
@@ -110,6 +112,7 @@ struct tsnep_rx {
dma_addr_t page_dma[TSNEP_RING_PAGE_COUNT];
struct tsnep_rx_entry entry[TSNEP_RING_SIZE];
+ int write;
int read;
u32 owner_counter;
int increment_owner_counter;
@@ -119,6 +122,7 @@ struct tsnep_rx {
u32 bytes;
u32 dropped;
u32 multicast;
+ u32 alloc_failed;
};
struct tsnep_queue {
@@ -132,6 +136,8 @@ struct tsnep_queue {
int irq;
u32 irq_mask;
+ void __iomem *irq_delay_addr;
+ u8 irq_delay;
};
struct tsnep_adapter {
@@ -223,5 +229,7 @@ static inline void tsnep_ethtool_self_test(struct net_device *dev,
#endif /* CONFIG_TSNEP_SELFTESTS */
void tsnep_get_system_time(struct tsnep_adapter *adapter, u64 *time);
+int tsnep_set_irq_coalesce(struct tsnep_queue *queue, u32 usecs);
+u32 tsnep_get_irq_coalesce(struct tsnep_queue *queue);
#endif /* _TSNEP_H */
diff --git a/drivers/net/ethernet/engleder/tsnep_ethtool.c b/drivers/net/ethernet/engleder/tsnep_ethtool.c
index a713a126b227..716815dad7d2 100644
--- a/drivers/net/ethernet/engleder/tsnep_ethtool.c
+++ b/drivers/net/ethernet/engleder/tsnep_ethtool.c
@@ -8,6 +8,7 @@ static const char tsnep_stats_strings[][ETH_GSTRING_LEN] = {
"rx_bytes",
"rx_dropped",
"rx_multicast",
+ "rx_alloc_failed",
"rx_phy_errors",
"rx_forwarded_phy_errors",
"rx_invalid_frame_errors",
@@ -21,6 +22,7 @@ struct tsnep_stats {
u64 rx_bytes;
u64 rx_dropped;
u64 rx_multicast;
+ u64 rx_alloc_failed;
u64 rx_phy_errors;
u64 rx_forwarded_phy_errors;
u64 rx_invalid_frame_errors;
@@ -36,6 +38,7 @@ static const char tsnep_rx_queue_stats_strings[][ETH_GSTRING_LEN] = {
"rx_%d_bytes",
"rx_%d_dropped",
"rx_%d_multicast",
+ "rx_%d_alloc_failed",
"rx_%d_no_descriptor_errors",
"rx_%d_buffer_too_small_errors",
"rx_%d_fifo_overflow_errors",
@@ -47,6 +50,7 @@ struct tsnep_rx_queue_stats {
u64 rx_bytes;
u64 rx_dropped;
u64 rx_multicast;
+ u64 rx_alloc_failed;
u64 rx_no_descriptor_errors;
u64 rx_buffer_too_small_errors;
u64 rx_fifo_overflow_errors;
@@ -178,6 +182,7 @@ static void tsnep_ethtool_get_ethtool_stats(struct net_device *netdev,
tsnep_stats.rx_bytes += adapter->rx[i].bytes;
tsnep_stats.rx_dropped += adapter->rx[i].dropped;
tsnep_stats.rx_multicast += adapter->rx[i].multicast;
+ tsnep_stats.rx_alloc_failed += adapter->rx[i].alloc_failed;
}
reg = ioread32(adapter->addr + ECM_STAT);
tsnep_stats.rx_phy_errors =
@@ -200,6 +205,8 @@ static void tsnep_ethtool_get_ethtool_stats(struct net_device *netdev,
tsnep_rx_queue_stats.rx_bytes = adapter->rx[i].bytes;
tsnep_rx_queue_stats.rx_dropped = adapter->rx[i].dropped;
tsnep_rx_queue_stats.rx_multicast = adapter->rx[i].multicast;
+ tsnep_rx_queue_stats.rx_alloc_failed =
+ adapter->rx[i].alloc_failed;
reg = ioread32(adapter->addr + TSNEP_QUEUE(i) +
TSNEP_RX_STATISTIC);
tsnep_rx_queue_stats.rx_no_descriptor_errors =
@@ -250,10 +257,10 @@ static int tsnep_ethtool_get_sset_count(struct net_device *netdev, int sset)
}
}
-static int tsnep_ethtool_get_rxnfc(struct net_device *dev,
+static int tsnep_ethtool_get_rxnfc(struct net_device *netdev,
struct ethtool_rxnfc *cmd, u32 *rule_locs)
{
- struct tsnep_adapter *adapter = netdev_priv(dev);
+ struct tsnep_adapter *adapter = netdev_priv(netdev);
switch (cmd->cmd) {
case ETHTOOL_GRXRINGS:
@@ -273,10 +280,10 @@ static int tsnep_ethtool_get_rxnfc(struct net_device *dev,
}
}
-static int tsnep_ethtool_set_rxnfc(struct net_device *dev,
+static int tsnep_ethtool_set_rxnfc(struct net_device *netdev,
struct ethtool_rxnfc *cmd)
{
- struct tsnep_adapter *adapter = netdev_priv(dev);
+ struct tsnep_adapter *adapter = netdev_priv(netdev);
switch (cmd->cmd) {
case ETHTOOL_SRXCLSRLINS:
@@ -288,10 +295,21 @@ static int tsnep_ethtool_set_rxnfc(struct net_device *dev,
}
}
-static int tsnep_ethtool_get_ts_info(struct net_device *dev,
+static void tsnep_ethtool_get_channels(struct net_device *netdev,
+ struct ethtool_channels *ch)
+{
+ struct tsnep_adapter *adapter = netdev_priv(netdev);
+
+ ch->max_rx = adapter->num_rx_queues;
+ ch->max_tx = adapter->num_tx_queues;
+ ch->rx_count = adapter->num_rx_queues;
+ ch->tx_count = adapter->num_tx_queues;
+}
+
+static int tsnep_ethtool_get_ts_info(struct net_device *netdev,
struct ethtool_ts_info *info)
{
- struct tsnep_adapter *adapter = netdev_priv(dev);
+ struct tsnep_adapter *adapter = netdev_priv(netdev);
info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE |
SOF_TIMESTAMPING_RX_SOFTWARE |
@@ -313,7 +331,137 @@ static int tsnep_ethtool_get_ts_info(struct net_device *dev,
return 0;
}
+static struct tsnep_queue *tsnep_get_queue_with_tx(struct tsnep_adapter *adapter,
+ int index)
+{
+ int i;
+
+ for (i = 0; i < adapter->num_queues; i++) {
+ if (adapter->queue[i].tx) {
+ if (index == 0)
+ return &adapter->queue[i];
+
+ index--;
+ }
+ }
+
+ return NULL;
+}
+
+static struct tsnep_queue *tsnep_get_queue_with_rx(struct tsnep_adapter *adapter,
+ int index)
+{
+ int i;
+
+ for (i = 0; i < adapter->num_queues; i++) {
+ if (adapter->queue[i].rx) {
+ if (index == 0)
+ return &adapter->queue[i];
+
+ index--;
+ }
+ }
+
+ return NULL;
+}
+
+static int tsnep_ethtool_get_coalesce(struct net_device *netdev,
+ struct ethtool_coalesce *ec,
+ struct kernel_ethtool_coalesce *kernel_coal,
+ struct netlink_ext_ack *extack)
+{
+ struct tsnep_adapter *adapter = netdev_priv(netdev);
+ struct tsnep_queue *queue;
+
+ queue = tsnep_get_queue_with_rx(adapter, 0);
+ if (queue)
+ ec->rx_coalesce_usecs = tsnep_get_irq_coalesce(queue);
+
+ queue = tsnep_get_queue_with_tx(adapter, 0);
+ if (queue)
+ ec->tx_coalesce_usecs = tsnep_get_irq_coalesce(queue);
+
+ return 0;
+}
+
+static int tsnep_ethtool_set_coalesce(struct net_device *netdev,
+ struct ethtool_coalesce *ec,
+ struct kernel_ethtool_coalesce *kernel_coal,
+ struct netlink_ext_ack *extack)
+{
+ struct tsnep_adapter *adapter = netdev_priv(netdev);
+ int i;
+ int retval;
+
+ for (i = 0; i < adapter->num_queues; i++) {
+ /* RX coalesce has priority for queues with TX and RX */
+ if (adapter->queue[i].rx)
+ retval = tsnep_set_irq_coalesce(&adapter->queue[i],
+ ec->rx_coalesce_usecs);
+ else
+ retval = tsnep_set_irq_coalesce(&adapter->queue[i],
+ ec->tx_coalesce_usecs);
+ if (retval != 0)
+ return retval;
+ }
+
+ return 0;
+}
+
+static int tsnep_ethtool_get_per_queue_coalesce(struct net_device *netdev,
+ u32 queue,
+ struct ethtool_coalesce *ec)
+{
+ struct tsnep_adapter *adapter = netdev_priv(netdev);
+ struct tsnep_queue *queue_with_rx;
+ struct tsnep_queue *queue_with_tx;
+
+ if (queue >= max(adapter->num_tx_queues, adapter->num_rx_queues))
+ return -EINVAL;
+
+ queue_with_rx = tsnep_get_queue_with_rx(adapter, queue);
+ if (queue_with_rx)
+ ec->rx_coalesce_usecs = tsnep_get_irq_coalesce(queue_with_rx);
+
+ queue_with_tx = tsnep_get_queue_with_tx(adapter, queue);
+ if (queue_with_tx)
+ ec->tx_coalesce_usecs = tsnep_get_irq_coalesce(queue_with_tx);
+
+ return 0;
+}
+
+static int tsnep_ethtool_set_per_queue_coalesce(struct net_device *netdev,
+ u32 queue,
+ struct ethtool_coalesce *ec)
+{
+ struct tsnep_adapter *adapter = netdev_priv(netdev);
+ struct tsnep_queue *queue_with_rx;
+ struct tsnep_queue *queue_with_tx;
+ int retval;
+
+ if (queue >= max(adapter->num_tx_queues, adapter->num_rx_queues))
+ return -EINVAL;
+
+ queue_with_rx = tsnep_get_queue_with_rx(adapter, queue);
+ if (queue_with_rx) {
+ retval = tsnep_set_irq_coalesce(queue_with_rx, ec->rx_coalesce_usecs);
+ if (retval != 0)
+ return retval;
+ }
+
+ /* RX coalesce has priority for queues with TX and RX */
+ queue_with_tx = tsnep_get_queue_with_tx(adapter, queue);
+ if (queue_with_tx && !queue_with_tx->rx) {
+ retval = tsnep_set_irq_coalesce(queue_with_tx, ec->tx_coalesce_usecs);
+ if (retval != 0)
+ return retval;
+ }
+
+ return 0;
+}
+
const struct ethtool_ops tsnep_ethtool_ops = {
+ .supported_coalesce_params = ETHTOOL_COALESCE_USECS,
.get_drvinfo = tsnep_ethtool_get_drvinfo,
.get_regs_len = tsnep_ethtool_get_regs_len,
.get_regs = tsnep_ethtool_get_regs,
@@ -327,7 +475,12 @@ const struct ethtool_ops tsnep_ethtool_ops = {
.get_sset_count = tsnep_ethtool_get_sset_count,
.get_rxnfc = tsnep_ethtool_get_rxnfc,
.set_rxnfc = tsnep_ethtool_set_rxnfc,
+ .get_channels = tsnep_ethtool_get_channels,
.get_ts_info = tsnep_ethtool_get_ts_info,
+ .get_coalesce = tsnep_ethtool_get_coalesce,
+ .set_coalesce = tsnep_ethtool_set_coalesce,
+ .get_per_queue_coalesce = tsnep_ethtool_get_per_queue_coalesce,
+ .set_per_queue_coalesce = tsnep_ethtool_set_per_queue_coalesce,
.get_link_ksettings = phy_ethtool_get_link_ksettings,
.set_link_ksettings = phy_ethtool_set_link_ksettings,
};
diff --git a/drivers/net/ethernet/engleder/tsnep_hw.h b/drivers/net/ethernet/engleder/tsnep_hw.h
index 315dada75323..55e1caf193a6 100644
--- a/drivers/net/ethernet/engleder/tsnep_hw.h
+++ b/drivers/net/ethernet/engleder/tsnep_hw.h
@@ -48,6 +48,13 @@
#define ECM_COUNTER_LOW 0x0028
#define ECM_COUNTER_HIGH 0x002C
+/* interrupt delay */
+#define ECM_INT_DELAY 0x0030
+#define ECM_INT_DELAY_MASK 0xF0
+#define ECM_INT_DELAY_SHIFT 4
+#define ECM_INT_DELAY_BASE_US 16
+#define ECM_INT_DELAY_OFFSET 1
+
/* control and status */
#define ECM_STATUS 0x0080
#define ECM_LINK_MODE_OFF 0x01000000
diff --git a/drivers/net/ethernet/engleder/tsnep_main.c b/drivers/net/ethernet/engleder/tsnep_main.c
index 13d5ff4e0e02..bf0190e1d2ea 100644
--- a/drivers/net/ethernet/engleder/tsnep_main.c
+++ b/drivers/net/ethernet/engleder/tsnep_main.c
@@ -39,6 +39,10 @@
#endif
#define DMA_ADDR_LOW(dma_addr) ((u32)((dma_addr) & 0xFFFFFFFF))
+#define TSNEP_COALESCE_USECS_DEFAULT 64
+#define TSNEP_COALESCE_USECS_MAX ((ECM_INT_DELAY_MASK >> ECM_INT_DELAY_SHIFT) * \
+ ECM_INT_DELAY_BASE_US + ECM_INT_DELAY_BASE_US - 1)
+
static void tsnep_enable_irq(struct tsnep_adapter *adapter, u32 mask)
{
iowrite32(mask, adapter->addr + ECM_INT_ENABLE);
@@ -83,6 +87,33 @@ static irqreturn_t tsnep_irq_txrx(int irq, void *arg)
return IRQ_HANDLED;
}
+int tsnep_set_irq_coalesce(struct tsnep_queue *queue, u32 usecs)
+{
+ if (usecs > TSNEP_COALESCE_USECS_MAX)
+ return -ERANGE;
+
+ usecs /= ECM_INT_DELAY_BASE_US;
+ usecs <<= ECM_INT_DELAY_SHIFT;
+ usecs &= ECM_INT_DELAY_MASK;
+
+ queue->irq_delay &= ~ECM_INT_DELAY_MASK;
+ queue->irq_delay |= usecs;
+ iowrite8(queue->irq_delay, queue->irq_delay_addr);
+
+ return 0;
+}
+
+u32 tsnep_get_irq_coalesce(struct tsnep_queue *queue)
+{
+ u32 usecs;
+
+ usecs = (queue->irq_delay & ECM_INT_DELAY_MASK);
+ usecs >>= ECM_INT_DELAY_SHIFT;
+ usecs *= ECM_INT_DELAY_BASE_US;
+
+ return usecs;
+}
+
static int tsnep_mdiobus_read(struct mii_bus *bus, int addr, int regnum)
{
struct tsnep_adapter *adapter = bus->priv;
@@ -629,23 +660,6 @@ static void tsnep_rx_ring_cleanup(struct tsnep_rx *rx)
}
}
-static int tsnep_rx_alloc_buffer(struct tsnep_rx *rx,
- struct tsnep_rx_entry *entry)
-{
- struct page *page;
-
- page = page_pool_dev_alloc_pages(rx->page_pool);
- if (unlikely(!page))
- return -ENOMEM;
-
- entry->page = page;
- entry->len = TSNEP_MAX_RX_BUF_SIZE;
- entry->dma = page_pool_get_dma_addr(entry->page);
- entry->desc->rx = __cpu_to_le64(entry->dma + TSNEP_SKB_PAD);
-
- return 0;
-}
-
static int tsnep_rx_ring_init(struct tsnep_rx *rx)
{
struct device *dmadev = rx->adapter->dmadev;
@@ -692,10 +706,6 @@ static int tsnep_rx_ring_init(struct tsnep_rx *rx)
entry = &rx->entry[i];
next_entry = &rx->entry[(i + 1) % TSNEP_RING_SIZE];
entry->desc->next = __cpu_to_le64(next_entry->desc_dma);
-
- retval = tsnep_rx_alloc_buffer(rx, entry);
- if (retval)
- goto failed;
}
return 0;
@@ -705,6 +715,45 @@ failed:
return retval;
}
+static int tsnep_rx_desc_available(struct tsnep_rx *rx)
+{
+ if (rx->read <= rx->write)
+ return TSNEP_RING_SIZE - rx->write + rx->read - 1;
+ else
+ return rx->read - rx->write - 1;
+}
+
+static void tsnep_rx_set_page(struct tsnep_rx *rx, struct tsnep_rx_entry *entry,
+ struct page *page)
+{
+ entry->page = page;
+ entry->len = TSNEP_MAX_RX_BUF_SIZE;
+ entry->dma = page_pool_get_dma_addr(entry->page);
+ entry->desc->rx = __cpu_to_le64(entry->dma + TSNEP_SKB_PAD);
+}
+
+static int tsnep_rx_alloc_buffer(struct tsnep_rx *rx, int index)
+{
+ struct tsnep_rx_entry *entry = &rx->entry[index];
+ struct page *page;
+
+ page = page_pool_dev_alloc_pages(rx->page_pool);
+ if (unlikely(!page))
+ return -ENOMEM;
+ tsnep_rx_set_page(rx, entry, page);
+
+ return 0;
+}
+
+static void tsnep_rx_reuse_buffer(struct tsnep_rx *rx, int index)
+{
+ struct tsnep_rx_entry *entry = &rx->entry[index];
+ struct tsnep_rx_entry *read = &rx->entry[rx->read];
+
+ tsnep_rx_set_page(rx, entry, read->page);
+ read->page = NULL;
+}
+
static void tsnep_rx_activate(struct tsnep_rx *rx, int index)
{
struct tsnep_rx_entry *entry = &rx->entry[index];
@@ -732,6 +781,48 @@ static void tsnep_rx_activate(struct tsnep_rx *rx, int index)
entry->desc->properties = __cpu_to_le32(entry->properties);
}
+static int tsnep_rx_refill(struct tsnep_rx *rx, int count, bool reuse)
+{
+ int index;
+ bool alloc_failed = false;
+ bool enable = false;
+ int i;
+ int retval;
+
+ for (i = 0; i < count && !alloc_failed; i++) {
+ index = (rx->write + i) % TSNEP_RING_SIZE;
+
+ retval = tsnep_rx_alloc_buffer(rx, index);
+ if (unlikely(retval)) {
+ rx->alloc_failed++;
+ alloc_failed = true;
+
+ /* reuse only if no other allocation was successful */
+ if (i == 0 && reuse)
+ tsnep_rx_reuse_buffer(rx, index);
+ else
+ break;
+ }
+
+ tsnep_rx_activate(rx, index);
+
+ enable = true;
+ }
+
+ if (enable) {
+ rx->write = (rx->write + i) % TSNEP_RING_SIZE;
+
+ /* descriptor properties shall be valid before hardware is
+ * notified
+ */
+ dma_wmb();
+
+ iowrite32(TSNEP_CONTROL_RX_ENABLE, rx->addr + TSNEP_CONTROL);
+ }
+
+ return i;
+}
+
static struct sk_buff *tsnep_build_skb(struct tsnep_rx *rx, struct page *page,
int length)
{
@@ -767,23 +858,42 @@ static int tsnep_rx_poll(struct tsnep_rx *rx, struct napi_struct *napi,
int budget)
{
struct device *dmadev = rx->adapter->dmadev;
+ int desc_available;
int done = 0;
enum dma_data_direction dma_dir;
struct tsnep_rx_entry *entry;
- struct page *page;
struct sk_buff *skb;
int length;
- bool enable = false;
- int retval;
+ desc_available = tsnep_rx_desc_available(rx);
dma_dir = page_pool_get_dma_dir(rx->page_pool);
- while (likely(done < budget)) {
+ while (likely(done < budget) && (rx->read != rx->write)) {
entry = &rx->entry[rx->read];
if ((__le32_to_cpu(entry->desc_wb->properties) &
TSNEP_DESC_OWNER_COUNTER_MASK) !=
(entry->properties & TSNEP_DESC_OWNER_COUNTER_MASK))
break;
+ done++;
+
+ if (desc_available >= TSNEP_RING_RX_REFILL) {
+ bool reuse = desc_available >= TSNEP_RING_RX_REUSE;
+
+ desc_available -= tsnep_rx_refill(rx, desc_available,
+ reuse);
+ if (!entry->page) {
+ /* buffer has been reused for refill to prevent
+ * empty RX ring, thus buffer cannot be used for
+ * RX processing
+ */
+ rx->read = (rx->read + 1) % TSNEP_RING_SIZE;
+ desc_available++;
+
+ rx->dropped++;
+
+ continue;
+ }
+ }
/* descriptor properties shall be read first, because valid data
* is signaled there
@@ -795,49 +905,30 @@ static int tsnep_rx_poll(struct tsnep_rx *rx, struct napi_struct *napi,
TSNEP_DESC_LENGTH_MASK;
dma_sync_single_range_for_cpu(dmadev, entry->dma, TSNEP_SKB_PAD,
length, dma_dir);
- page = entry->page;
- /* forward skb only if allocation is successful, otherwise
- * page is reused and frame dropped
- */
- retval = tsnep_rx_alloc_buffer(rx, entry);
- if (!retval) {
- skb = tsnep_build_skb(rx, page, length);
- if (skb) {
- page_pool_release_page(rx->page_pool, page);
-
- rx->packets++;
- rx->bytes += length -
- TSNEP_RX_INLINE_METADATA_SIZE;
- if (skb->pkt_type == PACKET_MULTICAST)
- rx->multicast++;
-
- napi_gro_receive(napi, skb);
- } else {
- page_pool_recycle_direct(rx->page_pool, page);
+ rx->read = (rx->read + 1) % TSNEP_RING_SIZE;
+ desc_available++;
- rx->dropped++;
- }
- done++;
- } else {
- rx->dropped++;
- }
+ skb = tsnep_build_skb(rx, entry->page, length);
+ if (skb) {
+ page_pool_release_page(rx->page_pool, entry->page);
- tsnep_rx_activate(rx, rx->read);
+ rx->packets++;
+ rx->bytes += length - TSNEP_RX_INLINE_METADATA_SIZE;
+ if (skb->pkt_type == PACKET_MULTICAST)
+ rx->multicast++;
- enable = true;
+ napi_gro_receive(napi, skb);
+ } else {
+ page_pool_recycle_direct(rx->page_pool, entry->page);
- rx->read = (rx->read + 1) % TSNEP_RING_SIZE;
+ rx->dropped++;
+ }
+ entry->page = NULL;
}
- if (enable) {
- /* descriptor properties shall be valid before hardware is
- * notified
- */
- dma_wmb();
-
- iowrite32(TSNEP_CONTROL_RX_ENABLE, rx->addr + TSNEP_CONTROL);
- }
+ if (desc_available)
+ tsnep_rx_refill(rx, desc_available, false);
return done;
}
@@ -846,11 +937,13 @@ static bool tsnep_rx_pending(struct tsnep_rx *rx)
{
struct tsnep_rx_entry *entry;
- entry = &rx->entry[rx->read];
- if ((__le32_to_cpu(entry->desc_wb->properties) &
- TSNEP_DESC_OWNER_COUNTER_MASK) ==
- (entry->properties & TSNEP_DESC_OWNER_COUNTER_MASK))
- return true;
+ if (rx->read != rx->write) {
+ entry = &rx->entry[rx->read];
+ if ((__le32_to_cpu(entry->desc_wb->properties) &
+ TSNEP_DESC_OWNER_COUNTER_MASK) ==
+ (entry->properties & TSNEP_DESC_OWNER_COUNTER_MASK))
+ return true;
+ }
return false;
}
@@ -859,7 +952,6 @@ static int tsnep_rx_open(struct tsnep_adapter *adapter, void __iomem *addr,
int queue_index, struct tsnep_rx *rx)
{
dma_addr_t dma;
- int i;
int retval;
memset(rx, 0, sizeof(*rx));
@@ -877,13 +969,7 @@ static int tsnep_rx_open(struct tsnep_adapter *adapter, void __iomem *addr,
rx->owner_counter = 1;
rx->increment_owner_counter = TSNEP_RING_SIZE - 1;
- for (i = 0; i < TSNEP_RING_SIZE; i++)
- tsnep_rx_activate(rx, i);
-
- /* descriptor properties shall be valid before hardware is notified */
- dma_wmb();
-
- iowrite32(TSNEP_CONTROL_RX_ENABLE, rx->addr + TSNEP_CONTROL);
+ tsnep_rx_refill(rx, tsnep_rx_desc_available(rx), false);
return 0;
}
@@ -1371,6 +1457,11 @@ static int tsnep_queue_init(struct tsnep_adapter *adapter, int queue_count)
adapter->queue[0].tx = &adapter->tx[0];
adapter->queue[0].rx = &adapter->rx[0];
adapter->queue[0].irq_mask = irq_mask;
+ adapter->queue[0].irq_delay_addr = adapter->addr + ECM_INT_DELAY;
+ retval = tsnep_set_irq_coalesce(&adapter->queue[0],
+ TSNEP_COALESCE_USECS_DEFAULT);
+ if (retval < 0)
+ return retval;
adapter->netdev->irq = adapter->queue[0].irq;
@@ -1391,6 +1482,12 @@ static int tsnep_queue_init(struct tsnep_adapter *adapter, int queue_count)
adapter->queue[i].rx = &adapter->rx[i];
adapter->queue[i].irq_mask =
irq_mask << (ECM_INT_TXRX_SHIFT * i);
+ adapter->queue[i].irq_delay_addr =
+ adapter->addr + ECM_INT_DELAY + ECM_INT_DELAY_OFFSET * i;
+ retval = tsnep_set_irq_coalesce(&adapter->queue[i],
+ TSNEP_COALESCE_USECS_DEFAULT);
+ if (retval < 0)
+ return retval;
}
return 0;
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth-devlink.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth-devlink.c
index 5c6dd3029e2f..76f808d38066 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth-devlink.c
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth-devlink.c
@@ -37,18 +37,9 @@ static int dpaa2_eth_dl_info_get(struct devlink *devlink,
struct dpaa2_eth_devlink_priv *dl_priv = devlink_priv(devlink);
struct dpaa2_eth_priv *priv = dl_priv->dpaa2_priv;
char buf[10];
- int err;
-
- err = devlink_info_driver_name_put(req, KBUILD_MODNAME);
- if (err)
- return err;
scnprintf(buf, 10, "%d.%d", priv->dpni_ver_major, priv->dpni_ver_minor);
- err = devlink_info_version_running_put(req, "dpni", buf);
- if (err)
- return err;
-
- return 0;
+ return devlink_info_version_running_put(req, "dpni", buf);
}
static struct dpaa2_eth_trap_item *
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
index 97e1856641b4..0c35abb7d065 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
@@ -2147,8 +2147,11 @@ static int dpaa2_eth_link_state_update(struct dpaa2_eth_priv *priv)
/* When we manage the MAC/PHY using phylink there is no need
* to manually update the netif_carrier.
+ * We can avoid locking because we are called from the "link changed"
+ * IRQ handler, which is the same as the "endpoint changed" IRQ handler
+ * (the writer to priv->mac), so we cannot race with it.
*/
- if (dpaa2_eth_is_type_phy(priv))
+ if (dpaa2_mac_is_type_phy(priv->mac))
goto out;
/* Chech link state; speed / duplex changes are not treated yet */
@@ -2179,6 +2182,8 @@ static int dpaa2_eth_open(struct net_device *net_dev)
dpaa2_eth_seed_pools(priv);
+ mutex_lock(&priv->mac_lock);
+
if (!dpaa2_eth_is_type_phy(priv)) {
/* We'll only start the txqs when the link is actually ready;
* make sure we don't race against the link up notification,
@@ -2197,14 +2202,15 @@ static int dpaa2_eth_open(struct net_device *net_dev)
err = dpni_enable(priv->mc_io, 0, priv->mc_token);
if (err < 0) {
+ mutex_unlock(&priv->mac_lock);
netdev_err(net_dev, "dpni_enable() failed\n");
goto enable_err;
}
- if (dpaa2_eth_is_type_phy(priv)) {
+ if (dpaa2_eth_is_type_phy(priv))
dpaa2_mac_start(priv->mac);
- phylink_start(priv->mac->phylink);
- }
+
+ mutex_unlock(&priv->mac_lock);
return 0;
@@ -2277,14 +2283,17 @@ static int dpaa2_eth_stop(struct net_device *net_dev)
int dpni_enabled = 0;
int retries = 10;
+ mutex_lock(&priv->mac_lock);
+
if (dpaa2_eth_is_type_phy(priv)) {
- phylink_stop(priv->mac->phylink);
dpaa2_mac_stop(priv->mac);
} else {
netif_tx_stop_all_queues(net_dev);
netif_carrier_off(net_dev);
}
+ mutex_unlock(&priv->mac_lock);
+
/* On dpni_disable(), the MC firmware will:
* - stop MAC Rx and wait for all Rx frames to be enqueued to software
* - cut off WRIOP dequeues from egress FQs and wait until transmission
@@ -2610,12 +2619,20 @@ static int dpaa2_eth_ts_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
static int dpaa2_eth_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
struct dpaa2_eth_priv *priv = netdev_priv(dev);
+ int err;
if (cmd == SIOCSHWTSTAMP)
return dpaa2_eth_ts_ioctl(dev, rq, cmd);
- if (dpaa2_eth_is_type_phy(priv))
- return phylink_mii_ioctl(priv->mac->phylink, rq, cmd);
+ mutex_lock(&priv->mac_lock);
+
+ if (dpaa2_eth_is_type_phy(priv)) {
+ err = phylink_mii_ioctl(priv->mac->phylink, rq, cmd);
+ mutex_unlock(&priv->mac_lock);
+ return err;
+ }
+
+ mutex_unlock(&priv->mac_lock);
return -EOPNOTSUPP;
}
@@ -3791,7 +3808,7 @@ static int dpaa2_eth_setup_dpni(struct fsl_mc_device *ls_dev)
dev_err(dev, "DPNI version %u.%u not supported, need >= %u.%u\n",
priv->dpni_ver_major, priv->dpni_ver_minor,
DPNI_VER_MAJOR, DPNI_VER_MINOR);
- err = -ENOTSUPP;
+ err = -EOPNOTSUPP;
goto close;
}
@@ -4627,9 +4644,8 @@ static int dpaa2_eth_connect_mac(struct dpaa2_eth_priv *priv)
err = dpaa2_mac_open(mac);
if (err)
goto err_free_mac;
- priv->mac = mac;
- if (dpaa2_eth_is_type_phy(priv)) {
+ if (dpaa2_mac_is_type_phy(mac)) {
err = dpaa2_mac_connect(mac);
if (err) {
if (err == -EPROBE_DEFER)
@@ -4643,11 +4659,14 @@ static int dpaa2_eth_connect_mac(struct dpaa2_eth_priv *priv)
}
}
+ mutex_lock(&priv->mac_lock);
+ priv->mac = mac;
+ mutex_unlock(&priv->mac_lock);
+
return 0;
err_close_mac:
dpaa2_mac_close(mac);
- priv->mac = NULL;
err_free_mac:
kfree(mac);
return err;
@@ -4655,15 +4674,21 @@ err_free_mac:
static void dpaa2_eth_disconnect_mac(struct dpaa2_eth_priv *priv)
{
- if (dpaa2_eth_is_type_phy(priv))
- dpaa2_mac_disconnect(priv->mac);
+ struct dpaa2_mac *mac;
- if (!dpaa2_eth_has_mac(priv))
+ mutex_lock(&priv->mac_lock);
+ mac = priv->mac;
+ priv->mac = NULL;
+ mutex_unlock(&priv->mac_lock);
+
+ if (!mac)
return;
- dpaa2_mac_close(priv->mac);
- kfree(priv->mac);
- priv->mac = NULL;
+ if (dpaa2_mac_is_type_phy(mac))
+ dpaa2_mac_disconnect(mac);
+
+ dpaa2_mac_close(mac);
+ kfree(mac);
}
static irqreturn_t dpni_irq0_handler_thread(int irq_num, void *arg)
@@ -4673,6 +4698,7 @@ static irqreturn_t dpni_irq0_handler_thread(int irq_num, void *arg)
struct fsl_mc_device *dpni_dev = to_fsl_mc_device(dev);
struct net_device *net_dev = dev_get_drvdata(dev);
struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
+ bool had_mac;
int err;
err = dpni_get_irq_status(dpni_dev->mc_io, 0, dpni_dev->mc_handle,
@@ -4689,12 +4715,15 @@ static irqreturn_t dpni_irq0_handler_thread(int irq_num, void *arg)
dpaa2_eth_set_mac_addr(netdev_priv(net_dev));
dpaa2_eth_update_tx_fqids(priv);
- rtnl_lock();
- if (dpaa2_eth_has_mac(priv))
+ /* We can avoid locking because the "endpoint changed" IRQ
+ * handler is the only one who changes priv->mac at runtime,
+ * so we are not racing with anyone.
+ */
+ had_mac = !!priv->mac;
+ if (had_mac)
dpaa2_eth_disconnect_mac(priv);
else
dpaa2_eth_connect_mac(priv);
- rtnl_unlock();
}
return IRQ_HANDLED;
@@ -4792,6 +4821,8 @@ static int dpaa2_eth_probe(struct fsl_mc_device *dpni_dev)
priv->net_dev = net_dev;
SET_NETDEV_DEVLINK_PORT(net_dev, &priv->devlink_port);
+ mutex_init(&priv->mac_lock);
+
priv->iommu_domain = iommu_get_domain_for_dev(dev);
priv->tx_tstamp_type = HWTSTAMP_TX_OFF;
@@ -4899,6 +4930,10 @@ static int dpaa2_eth_probe(struct fsl_mc_device *dpni_dev)
}
#endif
+ err = dpaa2_eth_connect_mac(priv);
+ if (err)
+ goto err_connect_mac;
+
err = dpaa2_eth_setup_irqs(dpni_dev);
if (err) {
netdev_warn(net_dev, "Failed to set link interrupt, fall back to polling\n");
@@ -4911,10 +4946,6 @@ static int dpaa2_eth_probe(struct fsl_mc_device *dpni_dev)
priv->do_link_poll = true;
}
- err = dpaa2_eth_connect_mac(priv);
- if (err)
- goto err_connect_mac;
-
err = dpaa2_eth_dl_alloc(priv);
if (err)
goto err_dl_register;
@@ -4948,13 +4979,13 @@ err_dl_port_add:
err_dl_trap_register:
dpaa2_eth_dl_free(priv);
err_dl_register:
- dpaa2_eth_disconnect_mac(priv);
-err_connect_mac:
if (priv->do_link_poll)
kthread_stop(priv->poll_thread);
else
fsl_mc_free_irqs(dpni_dev);
err_poll_thread:
+ dpaa2_eth_disconnect_mac(priv);
+err_connect_mac:
dpaa2_eth_free_rings(priv);
err_alloc_rings:
err_csum:
@@ -5002,9 +5033,6 @@ static int dpaa2_eth_remove(struct fsl_mc_device *ls_dev)
#endif
unregister_netdev(net_dev);
- rtnl_lock();
- dpaa2_eth_disconnect_mac(priv);
- rtnl_unlock();
dpaa2_eth_dl_port_del(priv);
dpaa2_eth_dl_traps_unregister(priv);
@@ -5015,6 +5043,7 @@ static int dpaa2_eth_remove(struct fsl_mc_device *ls_dev)
else
fsl_mc_free_irqs(ls_dev);
+ dpaa2_eth_disconnect_mac(priv);
dpaa2_eth_free_rings(priv);
free_percpu(priv->fd);
free_percpu(priv->sgt_cache);
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h
index 5d0fc432e5b2..d56d7a13262e 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h
@@ -615,6 +615,8 @@ struct dpaa2_eth_priv {
#endif
struct dpaa2_mac *mac;
+ /* Serializes changes to priv->mac */
+ struct mutex mac_lock;
struct workqueue_struct *dpaa2_ptp_wq;
struct work_struct tx_onestep_tstamp;
struct sk_buff_head tx_skbs;
@@ -768,16 +770,15 @@ static inline unsigned int dpaa2_eth_rx_head_room(struct dpaa2_eth_priv *priv)
static inline bool dpaa2_eth_is_type_phy(struct dpaa2_eth_priv *priv)
{
- if (priv->mac &&
- (priv->mac->attr.link_type == DPMAC_LINK_TYPE_PHY ||
- priv->mac->attr.link_type == DPMAC_LINK_TYPE_BACKPLANE))
- return true;
+ lockdep_assert_held(&priv->mac_lock);
- return false;
+ return dpaa2_mac_is_type_phy(priv->mac);
}
static inline bool dpaa2_eth_has_mac(struct dpaa2_eth_priv *priv)
{
+ lockdep_assert_held(&priv->mac_lock);
+
return priv->mac ? true : false;
}
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c
index 32a38a03db57..e80e9388c71f 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c
@@ -85,11 +85,16 @@ static void dpaa2_eth_get_drvinfo(struct net_device *net_dev,
static int dpaa2_eth_nway_reset(struct net_device *net_dev)
{
struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
+ int err = -EOPNOTSUPP;
+
+ mutex_lock(&priv->mac_lock);
if (dpaa2_eth_is_type_phy(priv))
- return phylink_ethtool_nway_reset(priv->mac->phylink);
+ err = phylink_ethtool_nway_reset(priv->mac->phylink);
+
+ mutex_unlock(&priv->mac_lock);
- return -EOPNOTSUPP;
+ return err;
}
static int
@@ -97,10 +102,18 @@ dpaa2_eth_get_link_ksettings(struct net_device *net_dev,
struct ethtool_link_ksettings *link_settings)
{
struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
+ int err;
- if (dpaa2_eth_is_type_phy(priv))
- return phylink_ethtool_ksettings_get(priv->mac->phylink,
- link_settings);
+ mutex_lock(&priv->mac_lock);
+
+ if (dpaa2_eth_is_type_phy(priv)) {
+ err = phylink_ethtool_ksettings_get(priv->mac->phylink,
+ link_settings);
+ mutex_unlock(&priv->mac_lock);
+ return err;
+ }
+
+ mutex_unlock(&priv->mac_lock);
link_settings->base.autoneg = AUTONEG_DISABLE;
if (!(priv->link_state.options & DPNI_LINK_OPT_HALF_DUPLEX))
@@ -115,11 +128,17 @@ dpaa2_eth_set_link_ksettings(struct net_device *net_dev,
const struct ethtool_link_ksettings *link_settings)
{
struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
+ int err = -EOPNOTSUPP;
- if (!dpaa2_eth_is_type_phy(priv))
- return -ENOTSUPP;
+ mutex_lock(&priv->mac_lock);
- return phylink_ethtool_ksettings_set(priv->mac->phylink, link_settings);
+ if (dpaa2_eth_is_type_phy(priv))
+ err = phylink_ethtool_ksettings_set(priv->mac->phylink,
+ link_settings);
+
+ mutex_unlock(&priv->mac_lock);
+
+ return err;
}
static void dpaa2_eth_get_pauseparam(struct net_device *net_dev,
@@ -128,11 +147,16 @@ static void dpaa2_eth_get_pauseparam(struct net_device *net_dev,
struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
u64 link_options = priv->link_state.options;
+ mutex_lock(&priv->mac_lock);
+
if (dpaa2_eth_is_type_phy(priv)) {
phylink_ethtool_get_pauseparam(priv->mac->phylink, pause);
+ mutex_unlock(&priv->mac_lock);
return;
}
+ mutex_unlock(&priv->mac_lock);
+
pause->rx_pause = dpaa2_eth_rx_pause_enabled(link_options);
pause->tx_pause = dpaa2_eth_tx_pause_enabled(link_options);
pause->autoneg = AUTONEG_DISABLE;
@@ -151,9 +175,17 @@ static int dpaa2_eth_set_pauseparam(struct net_device *net_dev,
return -EOPNOTSUPP;
}
- if (dpaa2_eth_is_type_phy(priv))
- return phylink_ethtool_set_pauseparam(priv->mac->phylink,
- pause);
+ mutex_lock(&priv->mac_lock);
+
+ if (dpaa2_eth_is_type_phy(priv)) {
+ err = phylink_ethtool_set_pauseparam(priv->mac->phylink,
+ pause);
+ mutex_unlock(&priv->mac_lock);
+ return err;
+ }
+
+ mutex_unlock(&priv->mac_lock);
+
if (pause->autoneg)
return -EOPNOTSUPP;
@@ -185,7 +217,6 @@ static int dpaa2_eth_set_pauseparam(struct net_device *net_dev,
static void dpaa2_eth_get_strings(struct net_device *netdev, u32 stringset,
u8 *data)
{
- struct dpaa2_eth_priv *priv = netdev_priv(netdev);
u8 *p = data;
int i;
@@ -199,22 +230,17 @@ static void dpaa2_eth_get_strings(struct net_device *netdev, u32 stringset,
strscpy(p, dpaa2_ethtool_extras[i], ETH_GSTRING_LEN);
p += ETH_GSTRING_LEN;
}
- if (dpaa2_eth_has_mac(priv))
- dpaa2_mac_get_strings(p);
+ dpaa2_mac_get_strings(p);
break;
}
}
static int dpaa2_eth_get_sset_count(struct net_device *net_dev, int sset)
{
- int num_ss_stats = DPAA2_ETH_NUM_STATS + DPAA2_ETH_NUM_EXTRA_STATS;
- struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
-
switch (sset) {
case ETH_SS_STATS: /* ethtool_get_stats(), ethtool_get_drvinfo() */
- if (dpaa2_eth_has_mac(priv))
- num_ss_stats += dpaa2_mac_get_sset_count();
- return num_ss_stats;
+ return DPAA2_ETH_NUM_STATS + DPAA2_ETH_NUM_EXTRA_STATS +
+ dpaa2_mac_get_sset_count();
default:
return -EOPNOTSUPP;
}
@@ -315,8 +341,12 @@ static void dpaa2_eth_get_ethtool_stats(struct net_device *net_dev,
}
*(data + i++) = buf_cnt_total;
+ mutex_lock(&priv->mac_lock);
+
if (dpaa2_eth_has_mac(priv))
dpaa2_mac_get_ethtool_stats(priv->mac, data + i);
+
+ mutex_unlock(&priv->mac_lock);
}
static int dpaa2_eth_prep_eth_rule(struct ethhdr *eth_value, struct ethhdr *eth_mask,
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c
index 51c9da8e1be2..c886f33f8c6f 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c
@@ -338,12 +338,20 @@ static void dpaa2_mac_set_supported_interfaces(struct dpaa2_mac *mac)
void dpaa2_mac_start(struct dpaa2_mac *mac)
{
+ ASSERT_RTNL();
+
if (mac->serdes_phy)
phy_power_on(mac->serdes_phy);
+
+ phylink_start(mac->phylink);
}
void dpaa2_mac_stop(struct dpaa2_mac *mac)
{
+ ASSERT_RTNL();
+
+ phylink_stop(mac->phylink);
+
if (mac->serdes_phy)
phy_power_off(mac->serdes_phy);
}
@@ -422,7 +430,9 @@ int dpaa2_mac_connect(struct dpaa2_mac *mac)
}
mac->phylink = phylink;
+ rtnl_lock();
err = phylink_fwnode_phy_connect(mac->phylink, dpmac_node, 0);
+ rtnl_unlock();
if (err) {
netdev_err(net_dev, "phylink_fwnode_phy_connect() = %d\n", err);
goto err_phylink_destroy;
@@ -440,10 +450,10 @@ err_pcs_destroy:
void dpaa2_mac_disconnect(struct dpaa2_mac *mac)
{
- if (!mac->phylink)
- return;
-
+ rtnl_lock();
phylink_disconnect_phy(mac->phylink);
+ rtnl_unlock();
+
phylink_destroy(mac->phylink);
dpaa2_pcs_destroy(mac);
of_phy_put(mac->serdes_phy);
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h
index a58cab188a99..c1ec9efd413a 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h
@@ -30,8 +30,14 @@ struct dpaa2_mac {
struct phy *serdes_phy;
};
-bool dpaa2_mac_is_type_fixed(struct fsl_mc_device *dpmac_dev,
- struct fsl_mc_io *mc_io);
+static inline bool dpaa2_mac_is_type_phy(struct dpaa2_mac *mac)
+{
+ if (!mac)
+ return false;
+
+ return mac->attr.link_type == DPMAC_LINK_TYPE_PHY ||
+ mac->attr.link_type == DPMAC_LINK_TYPE_BACKPLANE;
+}
int dpaa2_mac_open(struct dpaa2_mac *mac);
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch-ethtool.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch-ethtool.c
index 720c9230cab5..6bc1988be311 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch-ethtool.c
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch-ethtool.c
@@ -60,11 +60,18 @@ dpaa2_switch_get_link_ksettings(struct net_device *netdev,
{
struct ethsw_port_priv *port_priv = netdev_priv(netdev);
struct dpsw_link_state state = {0};
- int err = 0;
+ int err;
+
+ mutex_lock(&port_priv->mac_lock);
+
+ if (dpaa2_switch_port_is_type_phy(port_priv)) {
+ err = phylink_ethtool_ksettings_get(port_priv->mac->phylink,
+ link_ksettings);
+ mutex_unlock(&port_priv->mac_lock);
+ return err;
+ }
- if (dpaa2_switch_port_is_type_phy(port_priv))
- return phylink_ethtool_ksettings_get(port_priv->mac->phylink,
- link_ksettings);
+ mutex_unlock(&port_priv->mac_lock);
err = dpsw_if_get_link_state(port_priv->ethsw_data->mc_io, 0,
port_priv->ethsw_data->dpsw_handle,
@@ -99,9 +106,16 @@ dpaa2_switch_set_link_ksettings(struct net_device *netdev,
bool if_running;
int err = 0, ret;
- if (dpaa2_switch_port_is_type_phy(port_priv))
- return phylink_ethtool_ksettings_set(port_priv->mac->phylink,
- link_ksettings);
+ mutex_lock(&port_priv->mac_lock);
+
+ if (dpaa2_switch_port_is_type_phy(port_priv)) {
+ err = phylink_ethtool_ksettings_set(port_priv->mac->phylink,
+ link_ksettings);
+ mutex_unlock(&port_priv->mac_lock);
+ return err;
+ }
+
+ mutex_unlock(&port_priv->mac_lock);
/* Interface needs to be down to change link settings */
if_running = netif_running(netdev);
@@ -145,14 +159,9 @@ dpaa2_switch_set_link_ksettings(struct net_device *netdev,
static int
dpaa2_switch_ethtool_get_sset_count(struct net_device *netdev, int sset)
{
- struct ethsw_port_priv *port_priv = netdev_priv(netdev);
- int num_ss_stats = DPAA2_SWITCH_NUM_COUNTERS;
-
switch (sset) {
case ETH_SS_STATS:
- if (port_priv->mac)
- num_ss_stats += dpaa2_mac_get_sset_count();
- return num_ss_stats;
+ return DPAA2_SWITCH_NUM_COUNTERS + dpaa2_mac_get_sset_count();
default:
return -EOPNOTSUPP;
}
@@ -161,7 +170,6 @@ dpaa2_switch_ethtool_get_sset_count(struct net_device *netdev, int sset)
static void dpaa2_switch_ethtool_get_strings(struct net_device *netdev,
u32 stringset, u8 *data)
{
- struct ethsw_port_priv *port_priv = netdev_priv(netdev);
u8 *p = data;
int i;
@@ -172,8 +180,7 @@ static void dpaa2_switch_ethtool_get_strings(struct net_device *netdev,
ETH_GSTRING_LEN);
p += ETH_GSTRING_LEN;
}
- if (port_priv->mac)
- dpaa2_mac_get_strings(p);
+ dpaa2_mac_get_strings(p);
break;
}
}
@@ -196,8 +203,12 @@ static void dpaa2_switch_ethtool_get_stats(struct net_device *netdev,
dpaa2_switch_ethtool_counters[i].name, err);
}
- if (port_priv->mac)
+ mutex_lock(&port_priv->mac_lock);
+
+ if (dpaa2_switch_port_has_mac(port_priv))
dpaa2_mac_get_ethtool_stats(port_priv->mac, data + i);
+
+ mutex_unlock(&port_priv->mac_lock);
}
const struct ethtool_ops dpaa2_switch_port_ethtool_ops = {
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c
index 14f739e04a3c..f4ae4289c41a 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c
@@ -602,8 +602,11 @@ static int dpaa2_switch_port_link_state_update(struct net_device *netdev)
/* When we manage the MAC/PHY using phylink there is no need
* to manually update the netif_carrier.
+ * We can avoid locking because we are called from the "link changed"
+ * IRQ handler, which is the same as the "endpoint changed" IRQ handler
+ * (the writer to port_priv->mac), so we cannot race with it.
*/
- if (dpaa2_switch_port_is_type_phy(port_priv))
+ if (dpaa2_mac_is_type_phy(port_priv->mac))
return 0;
/* Interrupts are received even though no one issued an 'ifconfig up'
@@ -683,6 +686,8 @@ static int dpaa2_switch_port_open(struct net_device *netdev)
struct ethsw_core *ethsw = port_priv->ethsw_data;
int err;
+ mutex_lock(&port_priv->mac_lock);
+
if (!dpaa2_switch_port_is_type_phy(port_priv)) {
/* Explicitly set carrier off, otherwise
* netif_carrier_ok() will return true and cause 'ip link show'
@@ -696,16 +701,17 @@ static int dpaa2_switch_port_open(struct net_device *netdev)
port_priv->ethsw_data->dpsw_handle,
port_priv->idx);
if (err) {
+ mutex_unlock(&port_priv->mac_lock);
netdev_err(netdev, "dpsw_if_enable err %d\n", err);
return err;
}
dpaa2_switch_enable_ctrl_if_napi(ethsw);
- if (dpaa2_switch_port_is_type_phy(port_priv)) {
+ if (dpaa2_switch_port_is_type_phy(port_priv))
dpaa2_mac_start(port_priv->mac);
- phylink_start(port_priv->mac->phylink);
- }
+
+ mutex_unlock(&port_priv->mac_lock);
return 0;
}
@@ -716,14 +722,17 @@ static int dpaa2_switch_port_stop(struct net_device *netdev)
struct ethsw_core *ethsw = port_priv->ethsw_data;
int err;
+ mutex_lock(&port_priv->mac_lock);
+
if (dpaa2_switch_port_is_type_phy(port_priv)) {
- phylink_stop(port_priv->mac->phylink);
dpaa2_mac_stop(port_priv->mac);
} else {
netif_tx_stop_all_queues(netdev);
netif_carrier_off(netdev);
}
+ mutex_unlock(&port_priv->mac_lock);
+
err = dpsw_if_disable(port_priv->ethsw_data->mc_io, 0,
port_priv->ethsw_data->dpsw_handle,
port_priv->idx);
@@ -1452,9 +1461,8 @@ static int dpaa2_switch_port_connect_mac(struct ethsw_port_priv *port_priv)
err = dpaa2_mac_open(mac);
if (err)
goto err_free_mac;
- port_priv->mac = mac;
- if (dpaa2_switch_port_is_type_phy(port_priv)) {
+ if (dpaa2_mac_is_type_phy(mac)) {
err = dpaa2_mac_connect(mac);
if (err) {
netdev_err(port_priv->netdev,
@@ -1464,11 +1472,14 @@ static int dpaa2_switch_port_connect_mac(struct ethsw_port_priv *port_priv)
}
}
+ mutex_lock(&port_priv->mac_lock);
+ port_priv->mac = mac;
+ mutex_unlock(&port_priv->mac_lock);
+
return 0;
err_close_mac:
dpaa2_mac_close(mac);
- port_priv->mac = NULL;
err_free_mac:
kfree(mac);
return err;
@@ -1476,15 +1487,21 @@ err_free_mac:
static void dpaa2_switch_port_disconnect_mac(struct ethsw_port_priv *port_priv)
{
- if (dpaa2_switch_port_is_type_phy(port_priv))
- dpaa2_mac_disconnect(port_priv->mac);
+ struct dpaa2_mac *mac;
+
+ mutex_lock(&port_priv->mac_lock);
+ mac = port_priv->mac;
+ port_priv->mac = NULL;
+ mutex_unlock(&port_priv->mac_lock);
- if (!dpaa2_switch_port_has_mac(port_priv))
+ if (!mac)
return;
- dpaa2_mac_close(port_priv->mac);
- kfree(port_priv->mac);
- port_priv->mac = NULL;
+ if (dpaa2_mac_is_type_phy(mac))
+ dpaa2_mac_disconnect(mac);
+
+ dpaa2_mac_close(mac);
+ kfree(mac);
}
static irqreturn_t dpaa2_switch_irq0_handler_thread(int irq_num, void *arg)
@@ -1494,6 +1511,7 @@ static irqreturn_t dpaa2_switch_irq0_handler_thread(int irq_num, void *arg)
struct ethsw_port_priv *port_priv;
u32 status = ~0;
int err, if_id;
+ bool had_mac;
err = dpsw_get_irq_status(ethsw->mc_io, 0, ethsw->dpsw_handle,
DPSW_IRQ_INDEX_IF, &status);
@@ -1511,12 +1529,15 @@ static irqreturn_t dpaa2_switch_irq0_handler_thread(int irq_num, void *arg)
}
if (status & DPSW_IRQ_EVENT_ENDPOINT_CHANGED) {
- rtnl_lock();
- if (dpaa2_switch_port_has_mac(port_priv))
+ /* We can avoid locking because the "endpoint changed" IRQ
+ * handler is the only one who changes priv->mac at runtime,
+ * so we are not racing with anyone.
+ */
+ had_mac = !!port_priv->mac;
+ if (had_mac)
dpaa2_switch_port_disconnect_mac(port_priv);
else
dpaa2_switch_port_connect_mac(port_priv);
- rtnl_unlock();
}
out:
@@ -2934,9 +2955,7 @@ static void dpaa2_switch_remove_port(struct ethsw_core *ethsw,
{
struct ethsw_port_priv *port_priv = ethsw->ports[port_idx];
- rtnl_lock();
dpaa2_switch_port_disconnect_mac(port_priv);
- rtnl_unlock();
free_netdev(port_priv->netdev);
ethsw->ports[port_idx] = NULL;
}
@@ -3255,6 +3274,8 @@ static int dpaa2_switch_probe_port(struct ethsw_core *ethsw,
port_priv->netdev = port_netdev;
port_priv->ethsw_data = ethsw;
+ mutex_init(&port_priv->mac_lock);
+
port_priv->idx = port_idx;
port_priv->stp_state = BR_STATE_FORWARDING;
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.h b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.h
index 0002dca4d417..42b3ca73f55d 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.h
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.h
@@ -161,6 +161,8 @@ struct ethsw_port_priv {
struct dpaa2_switch_filter_block *filter_block;
struct dpaa2_mac *mac;
+ /* Protects against changes to port_priv->mac */
+ struct mutex mac_lock;
};
/* Switch data */
@@ -230,12 +232,7 @@ static inline bool dpaa2_switch_supports_cpu_traffic(struct ethsw_core *ethsw)
static inline bool
dpaa2_switch_port_is_type_phy(struct ethsw_port_priv *port_priv)
{
- if (port_priv->mac &&
- (port_priv->mac->attr.link_type == DPMAC_LINK_TYPE_PHY ||
- port_priv->mac->attr.link_type == DPMAC_LINK_TYPE_BACKPLANE))
- return true;
-
- return false;
+ return dpaa2_mac_is_type_phy(port_priv->mac);
}
static inline bool dpaa2_switch_port_has_mac(struct ethsw_port_priv *port_priv)
diff --git a/drivers/net/ethernet/fungible/funeth/funeth_devlink.c b/drivers/net/ethernet/fungible/funeth/funeth_devlink.c
index d50c222948b4..4fbeb3fd71a8 100644
--- a/drivers/net/ethernet/fungible/funeth/funeth_devlink.c
+++ b/drivers/net/ethernet/fungible/funeth/funeth_devlink.c
@@ -3,14 +3,7 @@
#include "funeth.h"
#include "funeth_devlink.h"
-static int fun_dl_info_get(struct devlink *dl, struct devlink_info_req *req,
- struct netlink_ext_ack *extack)
-{
- return devlink_info_driver_name_put(req, KBUILD_MODNAME);
-}
-
static const struct devlink_ops fun_dl_ops = {
- .info_get = fun_dl_info_get,
};
struct devlink *fun_devlink_alloc(struct device *dev)
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_devlink.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_devlink.c
index 4c441e6a5082..3d3b69605423 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_devlink.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_devlink.c
@@ -13,11 +13,6 @@ static int hclge_devlink_info_get(struct devlink *devlink,
struct hclge_devlink_priv *priv = devlink_priv(devlink);
char version_str[HCLGE_DEVLINK_FW_STRING_LEN];
struct hclge_dev *hdev = priv->hdev;
- int ret;
-
- ret = devlink_info_driver_name_put(req, KBUILD_MODNAME);
- if (ret)
- return ret;
snprintf(version_str, sizeof(version_str), "%lu.%lu.%lu.%lu",
hnae3_get_field(hdev->fw_version, HNAE3_FW_VERSION_BYTE3_MASK,
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_devlink.c b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_devlink.c
index fdc19868b818..a6c3c5e8f0ab 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_devlink.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_devlink.c
@@ -13,11 +13,6 @@ static int hclgevf_devlink_info_get(struct devlink *devlink,
struct hclgevf_devlink_priv *priv = devlink_priv(devlink);
char version_str[HCLGEVF_DEVLINK_FW_STRING_LEN];
struct hclgevf_dev *hdev = priv->hdev;
- int ret;
-
- ret = devlink_info_driver_name_put(req, KBUILD_MODNAME);
- if (ret)
- return ret;
snprintf(version_str, sizeof(version_str), "%lu.%lu.%lu.%lu",
hnae3_get_field(hdev->fw_version, HNAE3_FW_VERSION_BYTE3_MASK,
diff --git a/drivers/net/ethernet/intel/ice/ice_devlink.c b/drivers/net/ethernet/intel/ice/ice_devlink.c
index 1d638216484d..8286e47b4bae 100644
--- a/drivers/net/ethernet/intel/ice/ice_devlink.c
+++ b/drivers/net/ethernet/intel/ice/ice_devlink.c
@@ -311,12 +311,6 @@ static int ice_devlink_info_get(struct devlink *devlink,
}
}
- err = devlink_info_driver_name_put(req, KBUILD_MODNAME);
- if (err) {
- NL_SET_ERR_MSG_MOD(extack, "Unable to set driver name");
- goto out_free_ctx;
- }
-
ice_info_get_dsn(pf, ctx);
err = devlink_info_serial_number_put(req, ctx->buf);
@@ -1596,21 +1590,22 @@ void ice_devlink_destroy_vf_port(struct ice_vf *vf)
#define ICE_DEVLINK_READ_BLK_SIZE (1024 * 1024)
+static const struct devlink_region_ops ice_nvm_region_ops;
+static const struct devlink_region_ops ice_sram_region_ops;
+
/**
* ice_devlink_nvm_snapshot - Capture a snapshot of the NVM flash contents
* @devlink: the devlink instance
- * @ops: the devlink region being snapshotted
+ * @ops: the devlink region to snapshot
* @extack: extended ACK response structure
* @data: on exit points to snapshot data buffer
*
- * This function is called in response to the DEVLINK_CMD_REGION_TRIGGER for
- * the nvm-flash devlink region. It captures a snapshot of the full NVM flash
- * contents, including both banks of flash. This snapshot can later be viewed
- * via the devlink-region interface.
+ * This function is called in response to a DEVLINK_CMD_REGION_NEW for either
+ * the nvm-flash or shadow-ram region.
*
- * It captures the flash using the FLASH_ONLY bit set when reading via
- * firmware, so it does not read the current Shadow RAM contents. For that,
- * use the shadow-ram region.
+ * It captures a snapshot of the NVM or Shadow RAM flash contents. This
+ * snapshot can then later be viewed via the DEVLINK_CMD_REGION_READ netlink
+ * interface.
*
* @returns zero on success, and updates the data pointer. Returns a non-zero
* error code on failure.
@@ -1622,17 +1617,27 @@ static int ice_devlink_nvm_snapshot(struct devlink *devlink,
struct ice_pf *pf = devlink_priv(devlink);
struct device *dev = ice_pf_to_dev(pf);
struct ice_hw *hw = &pf->hw;
+ bool read_shadow_ram;
u8 *nvm_data, *tmp, i;
u32 nvm_size, left;
s8 num_blks;
int status;
- nvm_size = hw->flash.flash_size;
+ if (ops == &ice_nvm_region_ops) {
+ read_shadow_ram = false;
+ nvm_size = hw->flash.flash_size;
+ } else if (ops == &ice_sram_region_ops) {
+ read_shadow_ram = true;
+ nvm_size = hw->flash.sr_words * 2u;
+ } else {
+ NL_SET_ERR_MSG_MOD(extack, "Unexpected region in snapshot function");
+ return -EOPNOTSUPP;
+ }
+
nvm_data = vzalloc(nvm_size);
if (!nvm_data)
return -ENOMEM;
-
num_blks = DIV_ROUND_UP(nvm_size, ICE_DEVLINK_READ_BLK_SIZE);
tmp = nvm_data;
left = nvm_size;
@@ -1656,7 +1661,7 @@ static int ice_devlink_nvm_snapshot(struct devlink *devlink,
}
status = ice_read_flat_nvm(hw, i * ICE_DEVLINK_READ_BLK_SIZE,
- &read_sz, tmp, false);
+ &read_sz, tmp, read_shadow_ram);
if (status) {
dev_dbg(dev, "ice_read_flat_nvm failed after reading %u bytes, err %d aq_err %d\n",
read_sz, status, hw->adminq.sq_last_status);
@@ -1677,62 +1682,69 @@ static int ice_devlink_nvm_snapshot(struct devlink *devlink,
}
/**
- * ice_devlink_sram_snapshot - Capture a snapshot of the Shadow RAM contents
+ * ice_devlink_nvm_read - Read a portion of NVM flash contents
* @devlink: the devlink instance
- * @ops: the devlink region being snapshotted
+ * @ops: the devlink region to snapshot
* @extack: extended ACK response structure
- * @data: on exit points to snapshot data buffer
+ * @offset: the offset to start at
+ * @size: the amount to read
+ * @data: the data buffer to read into
*
- * This function is called in response to the DEVLINK_CMD_REGION_TRIGGER for
- * the shadow-ram devlink region. It captures a snapshot of the shadow ram
- * contents. This snapshot can later be viewed via the devlink-region
- * interface.
+ * This function is called in response to DEVLINK_CMD_REGION_READ to directly
+ * read a section of the NVM contents.
+ *
+ * It reads from either the nvm-flash or shadow-ram region contents.
*
* @returns zero on success, and updates the data pointer. Returns a non-zero
* error code on failure.
*/
-static int
-ice_devlink_sram_snapshot(struct devlink *devlink,
- const struct devlink_region_ops __always_unused *ops,
- struct netlink_ext_ack *extack, u8 **data)
+static int ice_devlink_nvm_read(struct devlink *devlink,
+ const struct devlink_region_ops *ops,
+ struct netlink_ext_ack *extack,
+ u64 offset, u32 size, u8 *data)
{
struct ice_pf *pf = devlink_priv(devlink);
struct device *dev = ice_pf_to_dev(pf);
struct ice_hw *hw = &pf->hw;
- u8 *sram_data;
- u32 sram_size;
- int err;
+ bool read_shadow_ram;
+ u64 nvm_size;
+ int status;
- sram_size = hw->flash.sr_words * 2u;
- sram_data = vzalloc(sram_size);
- if (!sram_data)
- return -ENOMEM;
+ if (ops == &ice_nvm_region_ops) {
+ read_shadow_ram = false;
+ nvm_size = hw->flash.flash_size;
+ } else if (ops == &ice_sram_region_ops) {
+ read_shadow_ram = true;
+ nvm_size = hw->flash.sr_words * 2u;
+ } else {
+ NL_SET_ERR_MSG_MOD(extack, "Unexpected region in snapshot function");
+ return -EOPNOTSUPP;
+ }
- err = ice_acquire_nvm(hw, ICE_RES_READ);
- if (err) {
+ if (offset + size >= nvm_size) {
+ NL_SET_ERR_MSG_MOD(extack, "Cannot read beyond the region size");
+ return -ERANGE;
+ }
+
+ status = ice_acquire_nvm(hw, ICE_RES_READ);
+ if (status) {
dev_dbg(dev, "ice_acquire_nvm failed, err %d aq_err %d\n",
- err, hw->adminq.sq_last_status);
+ status, hw->adminq.sq_last_status);
NL_SET_ERR_MSG_MOD(extack, "Failed to acquire NVM semaphore");
- vfree(sram_data);
- return err;
+ return -EIO;
}
- /* Read from the Shadow RAM, rather than directly from NVM */
- err = ice_read_flat_nvm(hw, 0, &sram_size, sram_data, true);
- if (err) {
+ status = ice_read_flat_nvm(hw, (u32)offset, &size, data,
+ read_shadow_ram);
+ if (status) {
dev_dbg(dev, "ice_read_flat_nvm failed after reading %u bytes, err %d aq_err %d\n",
- sram_size, err, hw->adminq.sq_last_status);
- NL_SET_ERR_MSG_MOD(extack,
- "Failed to read Shadow RAM contents");
+ size, status, hw->adminq.sq_last_status);
+ NL_SET_ERR_MSG_MOD(extack, "Failed to read NVM contents");
ice_release_nvm(hw);
- vfree(sram_data);
- return err;
+ return -EIO;
}
-
ice_release_nvm(hw);
- *data = sram_data;
-
return 0;
}
@@ -1784,12 +1796,14 @@ static const struct devlink_region_ops ice_nvm_region_ops = {
.name = "nvm-flash",
.destructor = vfree,
.snapshot = ice_devlink_nvm_snapshot,
+ .read = ice_devlink_nvm_read,
};
static const struct devlink_region_ops ice_sram_region_ops = {
.name = "shadow-ram",
.destructor = vfree,
- .snapshot = ice_devlink_sram_snapshot,
+ .snapshot = ice_devlink_nvm_snapshot,
+ .read = ice_devlink_nvm_read,
};
static const struct devlink_region_ops ice_devcaps_region_ops = {
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c
index 88dee589cb21..bda1a6fa2ec4 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c
@@ -1547,14 +1547,7 @@ static int rvu_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode,
return 0;
}
-static int rvu_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req,
- struct netlink_ext_ack *extack)
-{
- return devlink_info_driver_name_put(req, DRV_NAME);
-}
-
static const struct devlink_ops rvu_devlink_ops = {
- .info_get = rvu_devlink_info_get,
.eswitch_mode_get = rvu_devlink_eswitch_mode_get,
.eswitch_mode_set = rvu_devlink_eswitch_mode_set,
};
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c
index 594029007f85..00aef8f5ac29 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c
@@ -490,7 +490,7 @@ static bool rvu_npc_exact_alloc_id(struct rvu *rvu, u32 *seq_id)
if (idx == table->tot_ids) {
mutex_unlock(&table->lock);
dev_err(rvu->dev, "%s: No space in id bitmap (%d)\n",
- __func__, bitmap_weight(table->id_bmap, table->tot_ids));
+ __func__, table->tot_ids);
return false;
}
@@ -1870,12 +1870,11 @@ int rvu_npc_exact_init(struct rvu *rvu)
/* Set capability to true */
rvu->hw->cap.npc_exact_match_enabled = true;
- table = kmalloc(sizeof(*table), GFP_KERNEL);
+ table = kzalloc(sizeof(*table), GFP_KERNEL);
if (!table)
return -ENOMEM;
dev_dbg(rvu->dev, "%s: Memory allocation for table success\n", __func__);
- memset(table, 0, sizeof(*table));
rvu->hw->table = table;
/* Read table size, ways and depth */
@@ -1899,24 +1898,24 @@ int rvu_npc_exact_init(struct rvu *rvu)
table_size = table->mem_table.depth * table->mem_table.ways;
/* Allocate bitmap for 4way 2K table */
- table->mem_table.bmap = devm_kcalloc(rvu->dev, BITS_TO_LONGS(table_size),
- sizeof(long), GFP_KERNEL);
+ table->mem_table.bmap = devm_bitmap_zalloc(rvu->dev, table_size,
+ GFP_KERNEL);
if (!table->mem_table.bmap)
return -ENOMEM;
dev_dbg(rvu->dev, "%s: Allocated bitmap for 4way 2K entry table\n", __func__);
/* Allocate bitmap for 32 entry mcam */
- table->cam_table.bmap = devm_kcalloc(rvu->dev, 1, sizeof(long), GFP_KERNEL);
+ table->cam_table.bmap = devm_bitmap_zalloc(rvu->dev, 32, GFP_KERNEL);
if (!table->cam_table.bmap)
return -ENOMEM;
dev_dbg(rvu->dev, "%s: Allocated bitmap for 32 entry cam\n", __func__);
- table->tot_ids = (table->mem_table.depth * table->mem_table.ways) + table->cam_table.depth;
- table->id_bmap = devm_kcalloc(rvu->dev, BITS_TO_LONGS(table->tot_ids),
- table->tot_ids, GFP_KERNEL);
+ table->tot_ids = table_size + table->cam_table.depth;
+ table->id_bmap = devm_bitmap_zalloc(rvu->dev, table->tot_ids,
+ GFP_KERNEL);
if (!table->id_bmap)
return -ENOMEM;
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_devlink.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_devlink.c
index 777a27047c8e..63ef7c41d18d 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_devlink.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_devlink.c
@@ -77,22 +77,7 @@ static const struct devlink_param otx2_dl_params[] = {
otx2_dl_mcam_count_validate),
};
-/* Devlink OPs */
-static int otx2_devlink_info_get(struct devlink *devlink,
- struct devlink_info_req *req,
- struct netlink_ext_ack *extack)
-{
- struct otx2_devlink *otx2_dl = devlink_priv(devlink);
- struct otx2_nic *pfvf = otx2_dl->pfvf;
-
- if (is_otx2_vf(pfvf->pcifunc))
- return devlink_info_driver_name_put(req, "rvu_nicvf");
-
- return devlink_info_driver_name_put(req, "rvu_nicpf");
-}
-
static const struct devlink_ops otx2_devlink_ops = {
- .info_get = otx2_devlink_info_get,
};
int otx2_register_dl(struct otx2_nic *pfvf)
diff --git a/drivers/net/ethernet/marvell/prestera/prestera_devlink.c b/drivers/net/ethernet/marvell/prestera/prestera_devlink.c
index 84ad05c9f12d..2a4c9df4eb79 100644
--- a/drivers/net/ethernet/marvell/prestera/prestera_devlink.c
+++ b/drivers/net/ethernet/marvell/prestera/prestera_devlink.c
@@ -355,11 +355,6 @@ static int prestera_dl_info_get(struct devlink *dl,
{
struct prestera_switch *sw = devlink_priv(dl);
char buf[16];
- int err;
-
- err = devlink_info_driver_name_put(req, PRESTERA_DRV_NAME);
- if (err)
- return err;
snprintf(buf, sizeof(buf), "%d.%d.%d",
sw->dev->fw_rev.maj,
diff --git a/drivers/net/ethernet/mediatek/mtk_wed_wo.c b/drivers/net/ethernet/mediatek/mtk_wed_wo.c
index 4754b6db009e..a219da85f4db 100644
--- a/drivers/net/ethernet/mediatek/mtk_wed_wo.c
+++ b/drivers/net/ethernet/mediatek/mtk_wed_wo.c
@@ -133,17 +133,18 @@ mtk_wed_wo_dequeue(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q, u32 *len,
static int
mtk_wed_wo_queue_refill(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q,
- gfp_t gfp, bool rx)
+ bool rx)
{
enum dma_data_direction dir = rx ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
int n_buf = 0;
spin_lock_bh(&q->lock);
while (q->queued < q->n_desc) {
- void *buf = page_frag_alloc(&q->cache, q->buf_size, gfp);
struct mtk_wed_wo_queue_entry *entry;
dma_addr_t addr;
+ void *buf;
+ buf = page_frag_alloc(&q->cache, q->buf_size, GFP_ATOMIC);
if (!buf)
break;
@@ -215,7 +216,7 @@ mtk_wed_wo_rx_run_queue(struct mtk_wed_wo *wo, struct mtk_wed_wo_queue *q)
mtk_wed_mcu_rx_unsolicited_event(wo, skb);
}
- if (mtk_wed_wo_queue_refill(wo, q, GFP_ATOMIC, true)) {
+ if (mtk_wed_wo_queue_refill(wo, q, true)) {
u32 index = (q->head - 1) % q->n_desc;
mtk_wed_wo_queue_kick(wo, q, index);
@@ -432,7 +433,7 @@ mtk_wed_wo_hardware_init(struct mtk_wed_wo *wo)
if (ret)
goto error;
- mtk_wed_wo_queue_refill(wo, &wo->q_tx, GFP_KERNEL, false);
+ mtk_wed_wo_queue_refill(wo, &wo->q_tx, false);
mtk_wed_wo_queue_reset(wo, &wo->q_tx);
regs.desc_base = MTK_WED_WO_CCIF_DUMMY5;
@@ -446,7 +447,7 @@ mtk_wed_wo_hardware_init(struct mtk_wed_wo *wo)
if (ret)
goto error;
- mtk_wed_wo_queue_refill(wo, &wo->q_rx, GFP_KERNEL, true);
+ mtk_wed_wo_queue_refill(wo, &wo->q_rx, true);
mtk_wed_wo_queue_reset(wo, &wo->q_rx);
/* rx queue irqmask */
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c
index e7a894ba5c3e..d3ca745d107d 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c
@@ -37,7 +37,6 @@
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/random.h>
-#include <linux/io-mapping.h>
#include <linux/mlx5/driver.h>
#include <linux/mlx5/eq.h>
#include <linux/debugfs.h>
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/devlink.c b/drivers/net/ethernet/mellanox/mlx5/core/devlink.c
index cc2ae427dcb0..751bc4a9edcf 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/devlink.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/devlink.c
@@ -46,10 +46,6 @@ mlx5_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req,
u32 running_fw, stored_fw;
int err;
- err = devlink_info_driver_name_put(req, KBUILD_MODNAME);
- if (err)
- return err;
-
err = devlink_info_version_fixed_put(req, "fw.psid", dev->board_id);
if (err)
return err;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h
index ff5b302531d5..65790ff58a74 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h
@@ -103,11 +103,11 @@ struct page_pool;
* size actually used at runtime, but it's not a problem when calculating static
* array sizes.
*/
-#define MLX5_UMR_MAX_MTT_SPACE \
+#define MLX5_UMR_MAX_FLEX_SPACE \
(ALIGN_DOWN(MLX5_SEND_WQE_MAX_SIZE - sizeof(struct mlx5e_umr_wqe), \
- MLX5_UMR_MTT_ALIGNMENT))
+ MLX5_UMR_FLEX_ALIGNMENT))
#define MLX5_MPWRQ_MAX_PAGES_PER_WQE \
- rounddown_pow_of_two(MLX5_UMR_MAX_MTT_SPACE / sizeof(struct mlx5_mtt))
+ rounddown_pow_of_two(MLX5_UMR_MAX_FLEX_SPACE / sizeof(struct mlx5_mtt))
#define MLX5E_MAX_RQ_NUM_MTTS \
(ALIGN_DOWN(U16_MAX, 4) * 2) /* Fits into u16 and aligned by WQEBB. */
@@ -160,7 +160,7 @@ struct page_pool;
(((wqe_size) - sizeof(struct mlx5e_umr_wqe)) / sizeof(struct mlx5_klm))
#define MLX5E_KLM_ENTRIES_PER_WQE(wqe_size)\
- ALIGN_DOWN(MLX5E_KLM_MAX_ENTRIES_PER_WQE(wqe_size), MLX5_UMR_KLM_ALIGNMENT)
+ ALIGN_DOWN(MLX5E_KLM_MAX_ENTRIES_PER_WQE(wqe_size), MLX5_UMR_KLM_NUM_ENTRIES_ALIGNMENT)
#define MLX5E_MAX_KLM_PER_WQE(mdev) \
MLX5E_KLM_ENTRIES_PER_WQE(MLX5_SEND_WQE_BB * mlx5e_get_max_sq_aligned_wqebbs(mdev))
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/params.c b/drivers/net/ethernet/mellanox/mlx5/core/en/params.c
index 9ec9662d1d0b..585bdc8383ee 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/params.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/params.c
@@ -107,7 +107,7 @@ u8 mlx5e_mpwrq_log_wqe_sz(struct mlx5_core_dev *mdev, u8 page_shift,
/* Keep in sync with MLX5_MPWRQ_MAX_PAGES_PER_WQE. */
max_wqe_size = mlx5e_get_max_sq_aligned_wqebbs(mdev) * MLX5_SEND_WQE_BB;
max_pages_per_wqe = ALIGN_DOWN(max_wqe_size - sizeof(struct mlx5e_umr_wqe),
- MLX5_UMR_MTT_ALIGNMENT) / umr_entry_size;
+ MLX5_UMR_FLEX_ALIGNMENT) / umr_entry_size;
max_log_mpwqe_size = ilog2(max_pages_per_wqe) + page_shift;
WARN_ON_ONCE(max_log_mpwqe_size < MLX5E_ORDER2_MAX_PACKET_MTU);
@@ -146,7 +146,7 @@ u16 mlx5e_mpwrq_umr_wqe_sz(struct mlx5_core_dev *mdev, u8 page_shift,
u16 umr_wqe_sz;
umr_wqe_sz = sizeof(struct mlx5e_umr_wqe) +
- ALIGN(pages_per_wqe * umr_entry_size, MLX5_UMR_MTT_ALIGNMENT);
+ ALIGN(pages_per_wqe * umr_entry_size, MLX5_UMR_FLEX_ALIGNMENT);
WARN_ON_ONCE(DIV_ROUND_UP(umr_wqe_sz, MLX5_SEND_WQE_DS) > MLX5_WQE_CTRL_DS_MASK);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/trap.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/trap.c
index 53b270f652b9..915ce201aeb2 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/trap.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/trap.c
@@ -3,6 +3,7 @@
#include "act.h"
#include "en/tc_priv.h"
+#include "eswitch.h"
static bool
tc_act_can_offload_trap(struct mlx5e_tc_act_parse_state *parse_state,
@@ -10,13 +11,6 @@ tc_act_can_offload_trap(struct mlx5e_tc_act_parse_state *parse_state,
int act_index,
struct mlx5_flow_attr *attr)
{
- struct netlink_ext_ack *extack = parse_state->extack;
-
- if (parse_state->flow_action->num_entries != 1) {
- NL_SET_ERR_MSG_MOD(extack, "action trap is supported as a sole action only");
- return false;
- }
-
return true;
}
@@ -27,7 +21,7 @@ tc_act_parse_trap(struct mlx5e_tc_act_parse_state *parse_state,
struct mlx5_flow_attr *attr)
{
attr->action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
- attr->flags |= MLX5_ATTR_FLAG_SLOW_PATH;
+ attr->dest_ft = mlx5_eswitch_get_slow_fdb(priv->mdev->priv.eswitch);
return 0;
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c
index a715601865d3..1b03ab03fc5a 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c
@@ -345,29 +345,27 @@ static void mlx5e_xfrm_free_state(struct xfrm_state *x)
kfree(sa_entry);
}
-int mlx5e_ipsec_init(struct mlx5e_priv *priv)
+void mlx5e_ipsec_init(struct mlx5e_priv *priv)
{
struct mlx5e_ipsec *ipsec;
- int ret;
+ int ret = -ENOMEM;
if (!mlx5_ipsec_device_caps(priv->mdev)) {
netdev_dbg(priv->netdev, "Not an IPSec offload device\n");
- return 0;
+ return;
}
ipsec = kzalloc(sizeof(*ipsec), GFP_KERNEL);
if (!ipsec)
- return -ENOMEM;
+ return;
hash_init(ipsec->sadb_rx);
spin_lock_init(&ipsec->sadb_rx_lock);
ipsec->mdev = priv->mdev;
ipsec->wq = alloc_ordered_workqueue("mlx5e_ipsec: %s", 0,
priv->netdev->name);
- if (!ipsec->wq) {
- ret = -ENOMEM;
+ if (!ipsec->wq)
goto err_wq;
- }
ret = mlx5e_accel_ipsec_fs_init(ipsec);
if (ret)
@@ -375,13 +373,14 @@ int mlx5e_ipsec_init(struct mlx5e_priv *priv)
priv->ipsec = ipsec;
netdev_dbg(priv->netdev, "IPSec attached to netdevice\n");
- return 0;
+ return;
err_fs_init:
destroy_workqueue(ipsec->wq);
err_wq:
kfree(ipsec);
- return (ret != -EOPNOTSUPP) ? ret : 0;
+ mlx5_core_err(priv->mdev, "IPSec initialization failed, %d\n", ret);
+ return;
}
void mlx5e_ipsec_cleanup(struct mlx5e_priv *priv)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h
index 16bcceec16c4..4c47347d0ee2 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h
@@ -146,7 +146,7 @@ struct mlx5e_ipsec_sa_entry {
struct mlx5e_ipsec_modify_state_work modify_work;
};
-int mlx5e_ipsec_init(struct mlx5e_priv *priv);
+void mlx5e_ipsec_init(struct mlx5e_priv *priv);
void mlx5e_ipsec_cleanup(struct mlx5e_priv *priv);
void mlx5e_ipsec_build_netdev(struct mlx5e_priv *priv);
@@ -174,9 +174,8 @@ mlx5e_ipsec_sa2dev(struct mlx5e_ipsec_sa_entry *sa_entry)
return sa_entry->ipsec->mdev;
}
#else
-static inline int mlx5e_ipsec_init(struct mlx5e_priv *priv)
+static inline void mlx5e_ipsec_init(struct mlx5e_priv *priv)
{
- return 0;
}
static inline void mlx5e_ipsec_cleanup(struct mlx5e_priv *priv)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c
index f900709639f6..9369a580743e 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c
@@ -186,7 +186,7 @@ static int mlx5e_macsec_aso_reg_mr(struct mlx5_core_dev *mdev, struct mlx5e_macs
return err;
}
- dma_device = &mdev->pdev->dev;
+ dma_device = mlx5_core_dma_dev(mdev);
dma_addr = dma_map_single(dma_device, umr->ctx, sizeof(umr->ctx), DMA_BIDIRECTIONAL);
err = dma_mapping_error(dma_device, dma_addr);
if (err) {
@@ -1299,12 +1299,12 @@ static void macsec_aso_build_wqe_ctrl_seg(struct mlx5e_macsec_aso *macsec_aso,
struct mlx5_wqe_aso_ctrl_seg *aso_ctrl,
struct mlx5_aso_ctrl_param *param)
{
+ struct mlx5e_macsec_umr *umr = macsec_aso->umr;
+
memset(aso_ctrl, 0, sizeof(*aso_ctrl));
- if (macsec_aso->umr->dma_addr) {
- aso_ctrl->va_l = cpu_to_be32(macsec_aso->umr->dma_addr | ASO_CTRL_READ_EN);
- aso_ctrl->va_h = cpu_to_be32((u64)macsec_aso->umr->dma_addr >> 32);
- aso_ctrl->l_key = cpu_to_be32(macsec_aso->umr->mkey);
- }
+ aso_ctrl->va_l = cpu_to_be32(umr->dma_addr | ASO_CTRL_READ_EN);
+ aso_ctrl->va_h = cpu_to_be32((u64)umr->dma_addr >> 32);
+ aso_ctrl->l_key = cpu_to_be32(umr->mkey);
if (!param)
return;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
index 217c8a478977..8d36e2de53a9 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
@@ -208,7 +208,7 @@ static u16 mlx5e_mpwrq_umr_octowords(u32 entries, enum mlx5e_mpwrq_umr_mode umr_
u8 umr_entry_size = mlx5e_mpwrq_umr_entry_size(umr_mode);
u32 sz;
- sz = ALIGN(entries * umr_entry_size, MLX5_UMR_MTT_ALIGNMENT);
+ sz = ALIGN(entries * umr_entry_size, MLX5_UMR_FLEX_ALIGNMENT);
return sz / MLX5_OCTWORD;
}
@@ -5238,10 +5238,6 @@ static int mlx5e_nic_init(struct mlx5_core_dev *mdev,
}
priv->fs = fs;
- err = mlx5e_ipsec_init(priv);
- if (err)
- mlx5_core_err(mdev, "IPSec initialization failed, %d\n", err);
-
err = mlx5e_ktls_init(priv);
if (err)
mlx5_core_err(mdev, "TLS initialization failed, %d\n", err);
@@ -5254,7 +5250,6 @@ static void mlx5e_nic_cleanup(struct mlx5e_priv *priv)
{
mlx5e_health_destroy_reporters(priv);
mlx5e_ktls_cleanup(priv);
- mlx5e_ipsec_cleanup(priv);
mlx5e_fs_cleanup(priv->fs);
}
@@ -5383,6 +5378,7 @@ static void mlx5e_nic_enable(struct mlx5e_priv *priv)
int err;
mlx5e_fs_init_l2_addr(priv->fs, netdev);
+ mlx5e_ipsec_init(priv);
err = mlx5e_macsec_init(priv);
if (err)
@@ -5446,6 +5442,7 @@ static void mlx5e_nic_disable(struct mlx5e_priv *priv)
mlx5_lag_remove_netdev(mdev, priv->netdev);
mlx5_vxlan_reset_to_default(mdev->vxlan);
mlx5e_macsec_cleanup(priv);
+ mlx5e_ipsec_cleanup(priv);
}
int mlx5e_update_nic_rx(struct mlx5e_priv *priv)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c
index 1b53e8852c86..623886462c10 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c
@@ -751,7 +751,6 @@ static int mlx5e_init_ul_rep(struct mlx5_core_dev *mdev,
struct net_device *netdev)
{
struct mlx5e_priv *priv = netdev_priv(netdev);
- int err;
priv->fs = mlx5e_fs_init(priv->profile, mdev,
!test_bit(MLX5E_STATE_DESTROYING, &priv->state));
@@ -760,10 +759,6 @@ static int mlx5e_init_ul_rep(struct mlx5_core_dev *mdev,
return -ENOMEM;
}
- err = mlx5e_ipsec_init(priv);
- if (err)
- mlx5_core_err(mdev, "Uplink rep IPsec initialization failed, %d\n", err);
-
mlx5e_vxlan_set_netdev_info(priv);
mlx5e_build_rep_params(netdev);
mlx5e_timestamp_init(priv);
@@ -773,7 +768,6 @@ static int mlx5e_init_ul_rep(struct mlx5_core_dev *mdev,
static void mlx5e_cleanup_rep(struct mlx5e_priv *priv)
{
mlx5e_fs_cleanup(priv->fs);
- mlx5e_ipsec_cleanup(priv);
}
static int mlx5e_create_rep_ttc_table(struct mlx5e_priv *priv)
@@ -1112,6 +1106,8 @@ static void mlx5e_uplink_rep_enable(struct mlx5e_priv *priv)
struct mlx5_core_dev *mdev = priv->mdev;
u16 max_mtu;
+ mlx5e_ipsec_init(priv);
+
netdev->min_mtu = ETH_MIN_MTU;
mlx5_query_port_max_mtu(priv->mdev, &max_mtu, 1);
netdev->max_mtu = MLX5E_HW2SW_MTU(&priv->channels.params, max_mtu);
@@ -1158,6 +1154,8 @@ static void mlx5e_uplink_rep_disable(struct mlx5e_priv *priv)
mlx5e_rep_tc_disable(priv);
mlx5_lag_remove_netdev(mdev, priv->netdev);
mlx5_vxlan_reset_to_default(mdev->vxlan);
+
+ mlx5e_ipsec_cleanup(priv);
}
static MLX5E_DEFINE_STATS_GRP(sw_rep, 0);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
index b1ea0b995d9c..c8820ab22169 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
@@ -593,8 +593,8 @@ static int mlx5e_build_shampo_hd_umr(struct mlx5e_rq *rq,
int headroom, i;
headroom = rq->buff.headroom;
- new_entries = klm_entries - (shampo->pi & (MLX5_UMR_KLM_ALIGNMENT - 1));
- entries = ALIGN(klm_entries, MLX5_UMR_KLM_ALIGNMENT);
+ new_entries = klm_entries - (shampo->pi & (MLX5_UMR_KLM_NUM_ENTRIES_ALIGNMENT - 1));
+ entries = ALIGN(klm_entries, MLX5_UMR_KLM_NUM_ENTRIES_ALIGNMENT);
wqe_bbs = MLX5E_KLM_UMR_WQEBBS(entries);
pi = mlx5e_icosq_get_next_pi(sq, wqe_bbs);
umr_wqe = mlx5_wq_cyc_get_wqe(&sq->wq, pi);
@@ -603,7 +603,7 @@ static int mlx5e_build_shampo_hd_umr(struct mlx5e_rq *rq,
for (i = 0; i < entries; i++, index++) {
dma_info = &shampo->info[index];
if (i >= klm_entries || (index < shampo->pi && shampo->pi - index <
- MLX5_UMR_KLM_ALIGNMENT))
+ MLX5_UMR_KLM_NUM_ENTRIES_ALIGNMENT))
goto update_klm;
header_offset = (index & (MLX5E_SHAMPO_WQ_HEADER_PER_PAGE - 1)) <<
MLX5E_SHAMPO_LOG_MAX_HEADER_ENTRY_SIZE;
@@ -668,8 +668,8 @@ static int mlx5e_alloc_rx_hd_mpwqe(struct mlx5e_rq *rq)
if (!klm_entries)
return 0;
- klm_entries += (shampo->pi & (MLX5_UMR_KLM_ALIGNMENT - 1));
- index = ALIGN_DOWN(shampo->pi, MLX5_UMR_KLM_ALIGNMENT);
+ klm_entries += (shampo->pi & (MLX5_UMR_KLM_NUM_ENTRIES_ALIGNMENT - 1));
+ index = ALIGN_DOWN(shampo->pi, MLX5_UMR_KLM_NUM_ENTRIES_ALIGNMENT);
entries_before = shampo->hd_per_wq - index;
if (unlikely(entries_before < klm_entries))
@@ -727,6 +727,17 @@ static int mlx5e_alloc_rx_mpwqe(struct mlx5e_rq *rq, u16 ix)
};
}
+ /* Pad if needed, in case the value set to ucseg->xlt_octowords
+ * in mlx5e_build_umr_wqe() needed alignment.
+ */
+ if (rq->mpwqe.pages_per_wqe & (MLX5_UMR_MTT_NUM_ENTRIES_ALIGNMENT - 1)) {
+ int pad = ALIGN(rq->mpwqe.pages_per_wqe, MLX5_UMR_MTT_NUM_ENTRIES_ALIGNMENT) -
+ rq->mpwqe.pages_per_wqe;
+
+ memset(&umr_wqe->inline_mtts[rq->mpwqe.pages_per_wqe], 0,
+ sizeof(*umr_wqe->inline_mtts) * pad);
+ }
+
bitmap_zero(wi->xdp_xmit_bitmap, rq->mpwqe.pages_per_wqe);
wi->consumed_strides = 0;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h
index 48241317a535..0db41fa4a9a6 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h
@@ -97,8 +97,8 @@ struct mlx5_flow_attr {
} lag;
/* keep this union last */
union {
- struct mlx5_esw_flow_attr esw_attr[0];
- struct mlx5_nic_flow_attr nic_attr[0];
+ DECLARE_FLEX_ARRAY(struct mlx5_esw_flow_attr, esw_attr);
+ DECLARE_FLEX_ARRAY(struct mlx5_nic_flow_attr, nic_attr);
};
};
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h
index 3029bc1c0dd0..42d9df417e20 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h
@@ -744,6 +744,11 @@ static inline int mlx5_eswitch_num_vfs(struct mlx5_eswitch *esw)
return 0;
}
+static inline struct mlx5_flow_table *
+mlx5_eswitch_get_slow_fdb(struct mlx5_eswitch *esw)
+{
+ return esw->fdb_table.offloads.slow_fdb;
+}
#else /* CONFIG_MLX5_ESWITCH */
/* eswitch API stubs */
static inline int mlx5_eswitch_init(struct mlx5_core_dev *dev) { return 0; }
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
index 8c6c9bcb3dc3..9b6fbb19c22a 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
@@ -248,7 +248,7 @@ esw_setup_slow_path_dest(struct mlx5_flow_destination *dest, struct mlx5_flow_ac
if (MLX5_CAP_ESW_FLOWTABLE_FDB(esw->dev, ignore_flow_level))
flow_act->flags |= FLOW_ACT_IGNORE_FLOW_LEVEL;
dest[i].type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE;
- dest[i].ft = esw->fdb_table.offloads.slow_fdb;
+ dest[i].ft = mlx5_eswitch_get_slow_fdb(esw);
}
static int
@@ -479,13 +479,15 @@ esw_setup_dests(struct mlx5_flow_destination *dest,
esw_src_port_rewrite_supported(esw))
attr->flags |= MLX5_ATTR_FLAG_SRC_REWRITE;
- if (attr->flags & MLX5_ATTR_FLAG_SAMPLE &&
- !(attr->flags & MLX5_ATTR_FLAG_SLOW_PATH)) {
- esw_setup_sampler_dest(dest, flow_act, attr->sample_attr.sampler_id, *i);
- (*i)++;
- } else if (attr->flags & MLX5_ATTR_FLAG_SLOW_PATH) {
+ if (attr->flags & MLX5_ATTR_FLAG_SLOW_PATH) {
esw_setup_slow_path_dest(dest, flow_act, esw, *i);
(*i)++;
+ goto out;
+ }
+
+ if (attr->flags & MLX5_ATTR_FLAG_SAMPLE) {
+ esw_setup_sampler_dest(dest, flow_act, attr->sample_attr.sampler_id, *i);
+ (*i)++;
} else if (attr->flags & MLX5_ATTR_FLAG_ACCEPT) {
esw_setup_accept_dest(dest, flow_act, chains, *i);
(*i)++;
@@ -506,6 +508,7 @@ esw_setup_dests(struct mlx5_flow_destination *dest,
}
}
+out:
return err;
}
@@ -1046,7 +1049,7 @@ mlx5_eswitch_add_send_to_vport_rule(struct mlx5_eswitch *on_esw,
if (rep->vport == MLX5_VPORT_UPLINK)
spec->flow_context.flow_source = MLX5_FLOW_CONTEXT_FLOW_SOURCE_LOCAL_VPORT;
- flow_rule = mlx5_add_flow_rules(on_esw->fdb_table.offloads.slow_fdb,
+ flow_rule = mlx5_add_flow_rules(mlx5_eswitch_get_slow_fdb(on_esw),
spec, &flow_act, &dest, 1);
if (IS_ERR(flow_rule))
esw_warn(on_esw->dev, "FDB: Failed to add send to vport rule err %ld\n",
@@ -1095,7 +1098,7 @@ mlx5_eswitch_add_send_to_vport_meta_rule(struct mlx5_eswitch *esw, u16 vport_num
mlx5_eswitch_get_vport_metadata_for_match(esw, vport_num));
dest.vport.num = vport_num;
- flow_rule = mlx5_add_flow_rules(esw->fdb_table.offloads.slow_fdb,
+ flow_rule = mlx5_add_flow_rules(mlx5_eswitch_get_slow_fdb(esw),
spec, &flow_act, &dest, 1);
if (IS_ERR(flow_rule))
esw_warn(esw->dev, "FDB: Failed to add send to vport meta rule vport %d, err %ld\n",
@@ -1248,7 +1251,7 @@ static int esw_add_fdb_peer_miss_rules(struct mlx5_eswitch *esw,
esw_set_peer_miss_rule_source_port(esw, peer_dev->priv.eswitch,
spec, MLX5_VPORT_PF);
- flow = mlx5_add_flow_rules(esw->fdb_table.offloads.slow_fdb,
+ flow = mlx5_add_flow_rules(mlx5_eswitch_get_slow_fdb(esw),
spec, &flow_act, &dest, 1);
if (IS_ERR(flow)) {
err = PTR_ERR(flow);
@@ -1260,7 +1263,7 @@ static int esw_add_fdb_peer_miss_rules(struct mlx5_eswitch *esw,
if (mlx5_ecpf_vport_exists(esw->dev)) {
vport = mlx5_eswitch_get_vport(esw, MLX5_VPORT_ECPF);
MLX5_SET(fte_match_set_misc, misc, source_port, MLX5_VPORT_ECPF);
- flow = mlx5_add_flow_rules(esw->fdb_table.offloads.slow_fdb,
+ flow = mlx5_add_flow_rules(mlx5_eswitch_get_slow_fdb(esw),
spec, &flow_act, &dest, 1);
if (IS_ERR(flow)) {
err = PTR_ERR(flow);
@@ -1274,7 +1277,7 @@ static int esw_add_fdb_peer_miss_rules(struct mlx5_eswitch *esw,
peer_dev->priv.eswitch,
spec, vport->vport);
- flow = mlx5_add_flow_rules(esw->fdb_table.offloads.slow_fdb,
+ flow = mlx5_add_flow_rules(mlx5_eswitch_get_slow_fdb(esw),
spec, &flow_act, &dest, 1);
if (IS_ERR(flow)) {
err = PTR_ERR(flow);
@@ -1363,7 +1366,7 @@ static int esw_add_fdb_miss_rule(struct mlx5_eswitch *esw)
dest.vport.num = esw->manager_vport;
flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
- flow_rule = mlx5_add_flow_rules(esw->fdb_table.offloads.slow_fdb,
+ flow_rule = mlx5_add_flow_rules(mlx5_eswitch_get_slow_fdb(esw),
spec, &flow_act, &dest, 1);
if (IS_ERR(flow_rule)) {
err = PTR_ERR(flow_rule);
@@ -1378,7 +1381,7 @@ static int esw_add_fdb_miss_rule(struct mlx5_eswitch *esw)
dmac_v = MLX5_ADDR_OF(fte_match_param, headers_v,
outer_headers.dmac_47_16);
dmac_v[0] = 0x01;
- flow_rule = mlx5_add_flow_rules(esw->fdb_table.offloads.slow_fdb,
+ flow_rule = mlx5_add_flow_rules(mlx5_eswitch_get_slow_fdb(esw),
spec, &flow_act, &dest, 1);
if (IS_ERR(flow_rule)) {
err = PTR_ERR(flow_rule);
@@ -1927,7 +1930,7 @@ send_vport_err:
fdb_chains_err:
mlx5_destroy_flow_table(esw->fdb_table.offloads.tc_miss_table);
tc_miss_table_err:
- mlx5_destroy_flow_table(esw->fdb_table.offloads.slow_fdb);
+ mlx5_destroy_flow_table(mlx5_eswitch_get_slow_fdb(esw));
slow_fdb_err:
/* Holds true only as long as DMFS is the default */
mlx5_flow_namespace_set_mode(root_ns, MLX5_FLOW_STEERING_MODE_DMFS);
@@ -1938,7 +1941,7 @@ ns_err:
static void esw_destroy_offloads_fdb_tables(struct mlx5_eswitch *esw)
{
- if (!esw->fdb_table.offloads.slow_fdb)
+ if (!mlx5_eswitch_get_slow_fdb(esw))
return;
esw_debug(esw->dev, "Destroy offloads FDB Tables\n");
@@ -1954,7 +1957,7 @@ static void esw_destroy_offloads_fdb_tables(struct mlx5_eswitch *esw)
esw_chains_destroy(esw, esw_chains(esw));
mlx5_destroy_flow_table(esw->fdb_table.offloads.tc_miss_table);
- mlx5_destroy_flow_table(esw->fdb_table.offloads.slow_fdb);
+ mlx5_destroy_flow_table(mlx5_eswitch_get_slow_fdb(esw));
/* Holds true only as long as DMFS is the default */
mlx5_flow_namespace_set_mode(esw->fdb_table.offloads.ns,
MLX5_FLOW_STEERING_MODE_DMFS);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads_termtbl.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads_termtbl.c
index edd910258314..3a9a6bb9158d 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads_termtbl.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads_termtbl.c
@@ -210,6 +210,18 @@ static bool mlx5_eswitch_offload_is_uplink_port(const struct mlx5_eswitch *esw,
return (port_mask & port_value) == MLX5_VPORT_UPLINK;
}
+static bool
+mlx5_eswitch_is_push_vlan_no_cap(struct mlx5_eswitch *esw,
+ struct mlx5_flow_act *flow_act)
+{
+ if (flow_act->action & MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH &&
+ !(mlx5_fs_get_capabilities(esw->dev, MLX5_FLOW_NAMESPACE_FDB) &
+ MLX5_FLOW_STEERING_CAP_VLAN_PUSH_ON_RX))
+ return true;
+
+ return false;
+}
+
bool
mlx5_eswitch_termtbl_required(struct mlx5_eswitch *esw,
struct mlx5_flow_attr *attr,
@@ -225,10 +237,7 @@ mlx5_eswitch_termtbl_required(struct mlx5_eswitch *esw,
(!mlx5_eswitch_offload_is_uplink_port(esw, spec) && !esw_attr->int_port))
return false;
- /* push vlan on RX */
- if (flow_act->action & MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH &&
- !(mlx5_fs_get_capabilities(esw->dev, MLX5_FLOW_NAMESPACE_FDB) &
- MLX5_FLOW_STEERING_CAP_VLAN_PUSH_ON_RX))
+ if (mlx5_eswitch_is_push_vlan_no_cap(esw, flow_act))
return true;
/* hairpin */
@@ -252,19 +261,31 @@ mlx5_eswitch_add_termtbl_rule(struct mlx5_eswitch *esw,
struct mlx5_flow_act term_tbl_act = {};
struct mlx5_flow_handle *rule = NULL;
bool term_table_created = false;
+ bool is_push_vlan_on_rx;
int num_vport_dests = 0;
int i, curr_dest;
+ is_push_vlan_on_rx = mlx5_eswitch_is_push_vlan_no_cap(esw, flow_act);
mlx5_eswitch_termtbl_actions_move(flow_act, &term_tbl_act);
term_tbl_act.action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
for (i = 0; i < num_dest; i++) {
struct mlx5_termtbl_handle *tt;
+ bool hairpin = false;
/* only vport destinations can be terminated */
if (dest[i].type != MLX5_FLOW_DESTINATION_TYPE_VPORT)
continue;
+ if (attr->dests[num_vport_dests].rep &&
+ attr->dests[num_vport_dests].rep->vport == MLX5_VPORT_UPLINK)
+ hairpin = true;
+
+ if (!is_push_vlan_on_rx && !hairpin) {
+ num_vport_dests++;
+ continue;
+ }
+
if (attr->dests[num_vport_dests].flags & MLX5_ESW_DEST_ENCAP) {
term_tbl_act.action |= MLX5_FLOW_CONTEXT_ACTION_PACKET_REFORMAT;
term_tbl_act.pkt_reformat = attr->dests[num_vport_dests].pkt_reformat;
@@ -312,6 +333,9 @@ revert_changes:
for (curr_dest = 0; curr_dest < num_vport_dests; curr_dest++) {
struct mlx5_termtbl_handle *tt = attr->dests[curr_dest].termtbl;
+ if (!tt)
+ continue;
+
attr->dests[curr_dest].termtbl = NULL;
/* search for the destination associated with the
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/aso.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/aso.c
index c971ff04dd04..0f9e4f01c85a 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/lib/aso.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/aso.c
@@ -334,9 +334,6 @@ err_cq:
void mlx5_aso_destroy(struct mlx5_aso *aso)
{
- if (IS_ERR_OR_NULL(aso))
- return;
-
mlx5_aso_destroy_sq(aso);
mlx5_aso_destroy_cq(&aso->cq);
kfree(aso);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c
index 70e8dc305bec..7f5db13e3550 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c
@@ -37,7 +37,6 @@
#include <linux/pci.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
-#include <linux/io-mapping.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/mlx5/driver.h>
@@ -1604,8 +1603,6 @@ int mlx5_mdev_init(struct mlx5_core_dev *dev, int profile_idx)
int err;
memcpy(&dev->profile, &profile[profile_idx], sizeof(dev->profile));
- INIT_LIST_HEAD(&priv->ctx_list);
- spin_lock_init(&priv->ctx_lock);
lockdep_register_key(&dev->lock_key);
mutex_init(&dev->intf_state_mutex);
lockdep_set_class(&dev->intf_state_mutex, &dev->lock_key);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/uar.c b/drivers/net/ethernet/mellanox/mlx5/core/uar.c
index 8455e79bc44a..1513112ecec8 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/uar.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/uar.c
@@ -31,7 +31,6 @@
*/
#include <linux/kernel.h>
-#include <linux/io-mapping.h>
#include <linux/mlx5/driver.h>
#include "mlx5_core.h"
diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c
index a83f6bc30072..a0a06e2eff82 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/core.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/core.c
@@ -1459,11 +1459,6 @@ mlxsw_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req,
char buf[32];
int err;
- err = devlink_info_driver_name_put(req,
- mlxsw_core->bus_info->device_kind);
- if (err)
- return err;
-
mlxsw_reg_mgir_pack(mgir_pl);
err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mgir), mgir_pl);
if (err)
diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.c b/drivers/net/ethernet/microchip/vcap/vcap_api.c
index 93efa8243b02..f2435d7ab515 100644
--- a/drivers/net/ethernet/microchip/vcap/vcap_api.c
+++ b/drivers/net/ethernet/microchip/vcap/vcap_api.c
@@ -985,17 +985,12 @@ static u32 vcap_next_rule_addr(u32 addr, struct vcap_rule_internal *ri)
/* Assign a unique rule id and autogenerate one if id == 0 */
static u32 vcap_set_rule_id(struct vcap_rule_internal *ri)
{
- u32 next_id;
-
if (ri->data.id != 0)
return ri->data.id;
- next_id = ri->vctrl->rule_id + 1;
-
- for (next_id = ri->vctrl->rule_id + 1; next_id < ~0; ++next_id) {
+ for (u32 next_id = 1; next_id < ~0; ++next_id) {
if (!vcap_lookup_rule(ri->vctrl, next_id)) {
ri->data.id = next_id;
- ri->vctrl->rule_id = next_id;
break;
}
}
diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.h b/drivers/net/ethernet/microchip/vcap/vcap_api.h
index ca4499838306..689c7270f2a8 100644
--- a/drivers/net/ethernet/microchip/vcap/vcap_api.h
+++ b/drivers/net/ethernet/microchip/vcap/vcap_api.h
@@ -268,7 +268,6 @@ struct vcap_operations {
/* VCAP API Client control interface */
struct vcap_control {
- u32 rule_id; /* last used rule id (unique across VCAP instances) */
struct vcap_operations *ops; /* client supplied operations */
const struct vcap_info *vcaps; /* client supplied vcap models */
const struct vcap_statistics *stats; /* client supplied vcap stats */
diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c
index 5df00e940333..3b8d165dc832 100644
--- a/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c
+++ b/drivers/net/ethernet/microchip/vcap/vcap_api_debugfs.c
@@ -639,18 +639,25 @@ static int vcap_show_admin(struct vcap_control *vctrl,
mutex_lock(&admin->lock);
list_for_each_entry(elem, &admin->rules, list) {
ri = vcap_dup_rule(elem);
- if (IS_ERR(ri))
- goto free_rule;
+ if (IS_ERR(ri)) {
+ ret = PTR_ERR(ri);
+ goto err_unlock;
+ }
/* Read data from VCAP */
ret = vcap_read_rule(ri);
if (ret)
- goto free_rule;
+ goto err_free_rule;
out->prf(out->dst, "\n");
vcap_show_admin_rule(vctrl, admin, out, ri);
-free_rule:
vcap_free_rule((struct vcap_rule *)ri);
}
mutex_unlock(&admin->lock);
+ return 0;
+
+err_free_rule:
+ vcap_free_rule((struct vcap_rule *)ri);
+err_unlock:
+ mutex_unlock(&admin->lock);
return ret;
}
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_devlink.c b/drivers/net/ethernet/netronome/nfp/nfp_devlink.c
index 784f23602a8a..bf6bae557158 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_devlink.c
+++ b/drivers/net/ethernet/netronome/nfp/nfp_devlink.c
@@ -239,10 +239,6 @@ nfp_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req,
char *buf = NULL;
int err;
- err = devlink_info_driver_name_put(req, "nfp");
- if (err)
- return err;
-
vendor = nfp_hwinfo_lookup(pf->hwinfo, "assembly.vendor");
part = nfp_hwinfo_lookup(pf->hwinfo, "assembly.partno");
sn = nfp_hwinfo_lookup(pf->hwinfo, "assembly.serial");
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_devlink.c b/drivers/net/ethernet/pensando/ionic/ionic_devlink.c
index 567f778433e2..e6ff757895ab 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_devlink.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_devlink.c
@@ -26,10 +26,6 @@ static int ionic_dl_info_get(struct devlink *dl, struct devlink_info_req *req,
char buf[16];
int err = 0;
- err = devlink_info_driver_name_put(req, IONIC_DRV_NAME);
- if (err)
- return err;
-
err = devlink_info_version_running_put(req,
DEVLINK_INFO_VERSION_GENERIC_FW,
idev->dev_info.fw_version);
diff --git a/drivers/net/ethernet/qlogic/qed/qed_devlink.c b/drivers/net/ethernet/qlogic/qed/qed_devlink.c
index 6bb4e165b592..922c47797af6 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_devlink.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_devlink.c
@@ -162,10 +162,6 @@ static int qed_devlink_info_get(struct devlink *devlink,
dev_info = &cdev->common_dev_info;
- err = devlink_info_driver_name_put(req, KBUILD_MODNAME);
- if (err)
- return err;
-
memcpy(buf, cdev->hwfns[0].hw_info.part_num, sizeof(cdev->hwfns[0].hw_info.part_num));
buf[sizeof(cdev->hwfns[0].hw_info.part_num)] = 0;
diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
index 5bc1181f829b..a9dcc98b6af1 100644
--- a/drivers/net/ethernet/realtek/r8169_main.c
+++ b/drivers/net/ethernet/realtek/r8169_main.c
@@ -4559,12 +4559,13 @@ static int rtl8169_poll(struct napi_struct *napi, int budget)
static void r8169_phylink_handler(struct net_device *ndev)
{
struct rtl8169_private *tp = netdev_priv(ndev);
+ struct device *d = tp_to_dev(tp);
if (netif_carrier_ok(ndev)) {
rtl_link_chg_patch(tp);
- pm_request_resume(&tp->pci_dev->dev);
+ pm_request_resume(d);
} else {
- pm_runtime_idle(&tp->pci_dev->dev);
+ pm_runtime_idle(d);
}
phy_print_status(tp->phydev);
@@ -5282,6 +5283,8 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
dev->hw_features |= NETIF_F_RXALL;
dev->hw_features |= NETIF_F_RXFCS;
+ netdev_sw_irq_coalesce_default_on(dev);
+
/* configure chip for default features */
rtl8169_set_features(dev, dev->features);
diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig
index 31ff35174034..f77511fe4e87 100644
--- a/drivers/net/ethernet/stmicro/stmmac/Kconfig
+++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig
@@ -235,6 +235,15 @@ config DWMAC_INTEL_PLAT
the stmmac device driver. This driver is used for the Intel Keem Bay
SoC.
+config DWMAC_TEGRA
+ tristate "NVIDIA Tegra MGBE support"
+ depends on ARCH_TEGRA || COMPILE_TEST
+ help
+ This selects the Multi-GigaBit Ethernet (MGBE) Controller that is
+ found on the NVIDIA Tegra SoC devices. This driver provides the glue
+ layer on top of the stmmac driver required for these NVIDIA Tegra SoC
+ devices.
+
config DWMAC_VISCONTI
tristate "Toshiba Visconti DWMAC support"
default ARCH_VISCONTI
diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile
index d4e12e9ace4f..057e4bab5c08 100644
--- a/drivers/net/ethernet/stmicro/stmmac/Makefile
+++ b/drivers/net/ethernet/stmicro/stmmac/Makefile
@@ -31,6 +31,7 @@ obj-$(CONFIG_DWMAC_DWC_QOS_ETH) += dwmac-dwc-qos-eth.o
obj-$(CONFIG_DWMAC_INTEL_PLAT) += dwmac-intel-plat.o
obj-$(CONFIG_DWMAC_GENERIC) += dwmac-generic.o
obj-$(CONFIG_DWMAC_IMX8) += dwmac-imx.o
+obj-$(CONFIG_DWMAC_TEGRA) += dwmac-tegra.o
obj-$(CONFIG_DWMAC_VISCONTI) += dwmac-visconti.o
stmmac-platform-objs:= stmmac_platform.o
dwmac-altr-socfpga-objs := altr_tse_pcs.o dwmac-socfpga.o
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-tegra.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-tegra.c
new file mode 100644
index 000000000000..bdf990cf2f31
--- /dev/null
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-tegra.c
@@ -0,0 +1,388 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <linux/platform_device.h>
+#include <linux/of_device.h>
+#include <linux/module.h>
+#include <linux/stmmac.h>
+#include <linux/clk.h>
+
+#include "stmmac_platform.h"
+
+static const char *const mgbe_clks[] = {
+ "rx-pcs", "tx", "tx-pcs", "mac-divider", "mac", "mgbe", "ptp-ref", "mac"
+};
+
+struct tegra_mgbe {
+ struct device *dev;
+
+ struct clk_bulk_data *clks;
+
+ struct reset_control *rst_mac;
+ struct reset_control *rst_pcs;
+
+ void __iomem *hv;
+ void __iomem *regs;
+ void __iomem *xpcs;
+
+ struct mii_bus *mii;
+};
+
+#define XPCS_WRAP_UPHY_RX_CONTROL 0x801c
+#define XPCS_WRAP_UPHY_RX_CONTROL_RX_SW_OVRD BIT(31)
+#define XPCS_WRAP_UPHY_RX_CONTROL_RX_PCS_PHY_RDY BIT(10)
+#define XPCS_WRAP_UPHY_RX_CONTROL_RX_CDR_RESET BIT(9)
+#define XPCS_WRAP_UPHY_RX_CONTROL_RX_CAL_EN BIT(8)
+#define XPCS_WRAP_UPHY_RX_CONTROL_RX_SLEEP (BIT(7) | BIT(6))
+#define XPCS_WRAP_UPHY_RX_CONTROL_AUX_RX_IDDQ BIT(5)
+#define XPCS_WRAP_UPHY_RX_CONTROL_RX_IDDQ BIT(4)
+#define XPCS_WRAP_UPHY_RX_CONTROL_RX_DATA_EN BIT(0)
+#define XPCS_WRAP_UPHY_HW_INIT_CTRL 0x8020
+#define XPCS_WRAP_UPHY_HW_INIT_CTRL_TX_EN BIT(0)
+#define XPCS_WRAP_UPHY_HW_INIT_CTRL_RX_EN BIT(2)
+#define XPCS_WRAP_UPHY_STATUS 0x8044
+#define XPCS_WRAP_UPHY_STATUS_TX_P_UP BIT(0)
+#define XPCS_WRAP_IRQ_STATUS 0x8050
+#define XPCS_WRAP_IRQ_STATUS_PCS_LINK_STS BIT(6)
+
+#define XPCS_REG_ADDR_SHIFT 10
+#define XPCS_REG_ADDR_MASK 0x1fff
+#define XPCS_ADDR 0x3fc
+
+#define MGBE_WRAP_COMMON_INTR_ENABLE 0x8704
+#define MAC_SBD_INTR BIT(2)
+#define MGBE_WRAP_AXI_ASID0_CTRL 0x8400
+#define MGBE_SID 0x6
+
+static int __maybe_unused tegra_mgbe_suspend(struct device *dev)
+{
+ struct tegra_mgbe *mgbe = get_stmmac_bsp_priv(dev);
+ int err;
+
+ err = stmmac_suspend(dev);
+ if (err)
+ return err;
+
+ clk_bulk_disable_unprepare(ARRAY_SIZE(mgbe_clks), mgbe->clks);
+
+ return reset_control_assert(mgbe->rst_mac);
+}
+
+static int __maybe_unused tegra_mgbe_resume(struct device *dev)
+{
+ struct tegra_mgbe *mgbe = get_stmmac_bsp_priv(dev);
+ u32 value;
+ int err;
+
+ err = clk_bulk_prepare_enable(ARRAY_SIZE(mgbe_clks), mgbe->clks);
+ if (err < 0)
+ return err;
+
+ err = reset_control_deassert(mgbe->rst_mac);
+ if (err < 0)
+ return err;
+
+ /* Enable common interrupt at wrapper level */
+ writel(MAC_SBD_INTR, mgbe->regs + MGBE_WRAP_COMMON_INTR_ENABLE);
+
+ /* Program SID */
+ writel(MGBE_SID, mgbe->hv + MGBE_WRAP_AXI_ASID0_CTRL);
+
+ value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_STATUS);
+ if ((value & XPCS_WRAP_UPHY_STATUS_TX_P_UP) == 0) {
+ value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_HW_INIT_CTRL);
+ value |= XPCS_WRAP_UPHY_HW_INIT_CTRL_TX_EN;
+ writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_HW_INIT_CTRL);
+ }
+
+ err = readl_poll_timeout(mgbe->xpcs + XPCS_WRAP_UPHY_HW_INIT_CTRL, value,
+ (value & XPCS_WRAP_UPHY_HW_INIT_CTRL_TX_EN) == 0,
+ 500, 500 * 2000);
+ if (err < 0) {
+ dev_err(mgbe->dev, "timeout waiting for TX lane to become enabled\n");
+ clk_bulk_disable_unprepare(ARRAY_SIZE(mgbe_clks), mgbe->clks);
+ return err;
+ }
+
+ err = stmmac_resume(dev);
+ if (err < 0)
+ clk_bulk_disable_unprepare(ARRAY_SIZE(mgbe_clks), mgbe->clks);
+
+ return err;
+}
+
+static int mgbe_uphy_lane_bringup_serdes_up(struct net_device *ndev, void *mgbe_data)
+{
+ struct tegra_mgbe *mgbe = (struct tegra_mgbe *)mgbe_data;
+ u32 value;
+ int err;
+
+ value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
+ value |= XPCS_WRAP_UPHY_RX_CONTROL_RX_SW_OVRD;
+ writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
+
+ value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
+ value &= ~XPCS_WRAP_UPHY_RX_CONTROL_RX_IDDQ;
+ writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
+
+ value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
+ value &= ~XPCS_WRAP_UPHY_RX_CONTROL_AUX_RX_IDDQ;
+ writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
+
+ value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
+ value &= ~XPCS_WRAP_UPHY_RX_CONTROL_RX_SLEEP;
+ writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
+
+ value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
+ value |= XPCS_WRAP_UPHY_RX_CONTROL_RX_CAL_EN;
+ writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
+
+ err = readl_poll_timeout(mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL, value,
+ (value & XPCS_WRAP_UPHY_RX_CONTROL_RX_CAL_EN) == 0,
+ 1000, 1000 * 2000);
+ if (err < 0) {
+ dev_err(mgbe->dev, "timeout waiting for RX calibration to become enabled\n");
+ return err;
+ }
+
+ value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
+ value |= XPCS_WRAP_UPHY_RX_CONTROL_RX_DATA_EN;
+ writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
+
+ value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
+ value |= XPCS_WRAP_UPHY_RX_CONTROL_RX_CDR_RESET;
+ writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
+
+ value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
+ value &= ~XPCS_WRAP_UPHY_RX_CONTROL_RX_CDR_RESET;
+ writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
+
+ value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
+ value |= XPCS_WRAP_UPHY_RX_CONTROL_RX_PCS_PHY_RDY;
+ writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
+
+ err = readl_poll_timeout(mgbe->xpcs + XPCS_WRAP_IRQ_STATUS, value,
+ value & XPCS_WRAP_IRQ_STATUS_PCS_LINK_STS,
+ 500, 500 * 2000);
+ if (err < 0) {
+ dev_err(mgbe->dev, "timeout waiting for link to become ready\n");
+ return err;
+ }
+
+ /* clear status */
+ writel(value, mgbe->xpcs + XPCS_WRAP_IRQ_STATUS);
+
+ return 0;
+}
+
+static void mgbe_uphy_lane_bringup_serdes_down(struct net_device *ndev, void *mgbe_data)
+{
+ struct tegra_mgbe *mgbe = (struct tegra_mgbe *)mgbe_data;
+ u32 value;
+
+ value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
+ value |= XPCS_WRAP_UPHY_RX_CONTROL_RX_SW_OVRD;
+ writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
+
+ value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
+ value &= ~XPCS_WRAP_UPHY_RX_CONTROL_RX_DATA_EN;
+ writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
+
+ value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
+ value |= XPCS_WRAP_UPHY_RX_CONTROL_RX_SLEEP;
+ writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
+
+ value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
+ value |= XPCS_WRAP_UPHY_RX_CONTROL_AUX_RX_IDDQ;
+ writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
+
+ value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
+ value |= XPCS_WRAP_UPHY_RX_CONTROL_RX_IDDQ;
+ writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_RX_CONTROL);
+}
+
+static int tegra_mgbe_probe(struct platform_device *pdev)
+{
+ struct plat_stmmacenet_data *plat;
+ struct stmmac_resources res;
+ struct tegra_mgbe *mgbe;
+ int irq, err, i;
+ u32 value;
+
+ mgbe = devm_kzalloc(&pdev->dev, sizeof(*mgbe), GFP_KERNEL);
+ if (!mgbe)
+ return -ENOMEM;
+
+ mgbe->dev = &pdev->dev;
+
+ memset(&res, 0, sizeof(res));
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ mgbe->hv = devm_platform_ioremap_resource_byname(pdev, "hypervisor");
+ if (IS_ERR(mgbe->hv))
+ return PTR_ERR(mgbe->hv);
+
+ mgbe->regs = devm_platform_ioremap_resource_byname(pdev, "mac");
+ if (IS_ERR(mgbe->regs))
+ return PTR_ERR(mgbe->regs);
+
+ mgbe->xpcs = devm_platform_ioremap_resource_byname(pdev, "xpcs");
+ if (IS_ERR(mgbe->xpcs))
+ return PTR_ERR(mgbe->xpcs);
+
+ res.addr = mgbe->regs;
+ res.irq = irq;
+
+ mgbe->clks = devm_kzalloc(&pdev->dev, sizeof(*mgbe->clks), GFP_KERNEL);
+ if (!mgbe->clks)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(mgbe_clks); i++)
+ mgbe->clks[i].id = mgbe_clks[i];
+
+ err = devm_clk_bulk_get(mgbe->dev, ARRAY_SIZE(mgbe_clks), mgbe->clks);
+ if (err < 0)
+ return err;
+
+ err = clk_bulk_prepare_enable(ARRAY_SIZE(mgbe_clks), mgbe->clks);
+ if (err < 0)
+ return err;
+
+ /* Perform MAC reset */
+ mgbe->rst_mac = devm_reset_control_get(&pdev->dev, "mac");
+ if (IS_ERR(mgbe->rst_mac)) {
+ err = PTR_ERR(mgbe->rst_mac);
+ goto disable_clks;
+ }
+
+ err = reset_control_assert(mgbe->rst_mac);
+ if (err < 0)
+ goto disable_clks;
+
+ usleep_range(2000, 4000);
+
+ err = reset_control_deassert(mgbe->rst_mac);
+ if (err < 0)
+ goto disable_clks;
+
+ /* Perform PCS reset */
+ mgbe->rst_pcs = devm_reset_control_get(&pdev->dev, "pcs");
+ if (IS_ERR(mgbe->rst_pcs)) {
+ err = PTR_ERR(mgbe->rst_pcs);
+ goto disable_clks;
+ }
+
+ err = reset_control_assert(mgbe->rst_pcs);
+ if (err < 0)
+ goto disable_clks;
+
+ usleep_range(2000, 4000);
+
+ err = reset_control_deassert(mgbe->rst_pcs);
+ if (err < 0)
+ goto disable_clks;
+
+ plat = stmmac_probe_config_dt(pdev, res.mac);
+ if (IS_ERR(plat)) {
+ err = PTR_ERR(plat);
+ goto disable_clks;
+ }
+
+ plat->has_xgmac = 1;
+ plat->tso_en = 1;
+ plat->pmt = 1;
+ plat->bsp_priv = mgbe;
+
+ if (!plat->mdio_node)
+ plat->mdio_node = of_get_child_by_name(pdev->dev.of_node, "mdio");
+
+ if (!plat->mdio_bus_data) {
+ plat->mdio_bus_data = devm_kzalloc(&pdev->dev, sizeof(*plat->mdio_bus_data),
+ GFP_KERNEL);
+ if (!plat->mdio_bus_data) {
+ err = -ENOMEM;
+ goto remove;
+ }
+ }
+
+ plat->mdio_bus_data->needs_reset = true;
+
+ value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_STATUS);
+ if ((value & XPCS_WRAP_UPHY_STATUS_TX_P_UP) == 0) {
+ value = readl(mgbe->xpcs + XPCS_WRAP_UPHY_HW_INIT_CTRL);
+ value |= XPCS_WRAP_UPHY_HW_INIT_CTRL_TX_EN;
+ writel(value, mgbe->xpcs + XPCS_WRAP_UPHY_HW_INIT_CTRL);
+ }
+
+ err = readl_poll_timeout(mgbe->xpcs + XPCS_WRAP_UPHY_HW_INIT_CTRL, value,
+ (value & XPCS_WRAP_UPHY_HW_INIT_CTRL_TX_EN) == 0,
+ 500, 500 * 2000);
+ if (err < 0) {
+ dev_err(mgbe->dev, "timeout waiting for TX lane to become enabled\n");
+ goto remove;
+ }
+
+ plat->serdes_powerup = mgbe_uphy_lane_bringup_serdes_up;
+ plat->serdes_powerdown = mgbe_uphy_lane_bringup_serdes_down;
+
+ /* Tx FIFO Size - 128KB */
+ plat->tx_fifo_size = 131072;
+ /* Rx FIFO Size - 192KB */
+ plat->rx_fifo_size = 196608;
+
+ /* Enable common interrupt at wrapper level */
+ writel(MAC_SBD_INTR, mgbe->regs + MGBE_WRAP_COMMON_INTR_ENABLE);
+
+ /* Program SID */
+ writel(MGBE_SID, mgbe->hv + MGBE_WRAP_AXI_ASID0_CTRL);
+
+ plat->serdes_up_after_phy_linkup = 1;
+
+ err = stmmac_dvr_probe(&pdev->dev, plat, &res);
+ if (err < 0)
+ goto remove;
+
+ return 0;
+
+remove:
+ stmmac_remove_config_dt(pdev, plat);
+disable_clks:
+ clk_bulk_disable_unprepare(ARRAY_SIZE(mgbe_clks), mgbe->clks);
+
+ return err;
+}
+
+static int tegra_mgbe_remove(struct platform_device *pdev)
+{
+ struct tegra_mgbe *mgbe = get_stmmac_bsp_priv(&pdev->dev);
+
+ clk_bulk_disable_unprepare(ARRAY_SIZE(mgbe_clks), mgbe->clks);
+
+ stmmac_pltfr_remove(pdev);
+
+ return 0;
+}
+
+static const struct of_device_id tegra_mgbe_match[] = {
+ { .compatible = "nvidia,tegra234-mgbe", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, tegra_mgbe_match);
+
+static SIMPLE_DEV_PM_OPS(tegra_mgbe_pm_ops, tegra_mgbe_suspend, tegra_mgbe_resume);
+
+static struct platform_driver tegra_mgbe_driver = {
+ .probe = tegra_mgbe_probe,
+ .remove = tegra_mgbe_remove,
+ .driver = {
+ .name = "tegra-mgbe",
+ .pm = &tegra_mgbe_pm_ops,
+ .of_match_table = tegra_mgbe_match,
+ },
+};
+module_platform_driver(tegra_mgbe_driver);
+
+MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>");
+MODULE_DESCRIPTION("NVIDIA Tegra MGBE driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 18c7ca29da2c..f36590d0c830 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -988,6 +988,9 @@ static void stmmac_mac_link_up(struct phylink_config *config,
struct stmmac_priv *priv = netdev_priv(to_net_dev(config->dev));
u32 old_ctrl, ctrl;
+ if (priv->plat->serdes_up_after_phy_linkup && priv->plat->serdes_powerup)
+ priv->plat->serdes_powerup(priv->dev, priv->plat->bsp_priv);
+
old_ctrl = readl(priv->ioaddr + MAC_CTRL_REG);
ctrl = old_ctrl & ~priv->hw->link.speed_mask;
@@ -3809,7 +3812,7 @@ static int __stmmac_open(struct net_device *dev,
stmmac_reset_queues_param(priv);
- if (priv->plat->serdes_powerup) {
+ if (!priv->plat->serdes_up_after_phy_linkup && priv->plat->serdes_powerup) {
ret = priv->plat->serdes_powerup(dev, priv->plat->bsp_priv);
if (ret < 0) {
netdev_err(priv->dev, "%s: Serdes powerup failed\n",
@@ -7518,7 +7521,7 @@ int stmmac_resume(struct device *dev)
stmmac_mdio_reset(priv->mii);
}
- if (priv->plat->serdes_powerup) {
+ if (!priv->plat->serdes_up_after_phy_linkup && priv->plat->serdes_powerup) {
ret = priv->plat->serdes_powerup(ndev,
priv->plat->bsp_priv);
diff --git a/drivers/net/netdevsim/dev.c b/drivers/net/netdevsim/dev.c
index e14686594a71..b962fc8e1397 100644
--- a/drivers/net/netdevsim/dev.c
+++ b/drivers/net/netdevsim/dev.c
@@ -994,9 +994,6 @@ static int nsim_dev_info_get(struct devlink *devlink,
{
int err;
- err = devlink_info_driver_name_put(req, DRV_NAME);
- if (err)
- return err;
err = devlink_info_version_stored_put_ext(req, "fw.mgmt", "10.20.30",
DEVLINK_INFO_VERSION_TYPE_COMPONENT);
if (err)
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 8cff61dbc4b5..716870a4499c 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -1512,6 +1512,15 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
phy_resume(phydev);
phy_led_triggers_register(phydev);
+ /**
+ * If the external phy used by current mac interface is managed by
+ * another mac interface, so we should create a device link between
+ * phy dev and mac dev.
+ */
+ if (phydev->mdio.bus->parent && dev->dev.parent != phydev->mdio.bus->parent)
+ phydev->devlink = device_link_add(dev->dev.parent, &phydev->mdio.dev,
+ DL_FLAG_PM_RUNTIME | DL_FLAG_STATELESS);
+
return err;
error:
@@ -1750,6 +1759,9 @@ void phy_detach(struct phy_device *phydev)
struct module *ndev_owner = NULL;
struct mii_bus *bus;
+ if (phydev->devlink)
+ device_link_del(phydev->devlink);
+
if (phydev->sysfs_links) {
if (dev)
sysfs_remove_link(&dev->dev.kobj, "phydev");
diff --git a/drivers/net/thunderbolt.c b/drivers/net/thunderbolt.c
index b20cd370b7f2..4ed7f5b547e3 100644
--- a/drivers/net/thunderbolt.c
+++ b/drivers/net/thunderbolt.c
@@ -58,10 +58,10 @@
* supported then @frame_id is filled, otherwise it stays %0.
*/
struct thunderbolt_ip_frame_header {
- u32 frame_size;
- u16 frame_index;
- u16 frame_id;
- u32 frame_count;
+ __le32 frame_size;
+ __le16 frame_index;
+ __le16 frame_id;
+ __le32 frame_count;
};
enum thunderbolt_ip_frame_pdf {
@@ -1319,7 +1319,7 @@ static void tbnet_shutdown(struct tb_service *svc)
tbnet_tear_down(tb_service_get_drvdata(svc), true);
}
-static int __maybe_unused tbnet_suspend(struct device *dev)
+static int tbnet_suspend(struct device *dev)
{
struct tb_service *svc = tb_to_service(dev);
struct tbnet *net = tb_service_get_drvdata(svc);
@@ -1334,7 +1334,7 @@ static int __maybe_unused tbnet_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused tbnet_resume(struct device *dev)
+static int tbnet_resume(struct device *dev)
{
struct tb_service *svc = tb_to_service(dev);
struct tbnet *net = tb_service_get_drvdata(svc);
@@ -1350,9 +1350,7 @@ static int __maybe_unused tbnet_resume(struct device *dev)
return 0;
}
-static const struct dev_pm_ops tbnet_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(tbnet_suspend, tbnet_resume)
-};
+static DEFINE_SIMPLE_DEV_PM_OPS(tbnet_pm_ops, tbnet_suspend, tbnet_resume);
static const struct tb_service_id tbnet_ids[] = {
{ TB_SERVICE("network", 1) },
@@ -1364,7 +1362,7 @@ static struct tb_service_driver tbnet_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "thunderbolt-net",
- .pm = &tbnet_pm_ops,
+ .pm = pm_sleep_ptr(&tbnet_pm_ops),
},
.probe = tbnet_probe,
.remove = tbnet_remove,
diff --git a/drivers/net/wireless/ath/ath10k/Kconfig b/drivers/net/wireless/ath/ath10k/Kconfig
index ca007b800f75..e6ea884cafc1 100644
--- a/drivers/net/wireless/ath/ath10k/Kconfig
+++ b/drivers/net/wireless/ath/ath10k/Kconfig
@@ -44,6 +44,7 @@ config ATH10K_SNOC
tristate "Qualcomm ath10k SNOC support"
depends on ATH10K
depends on ARCH_QCOM || COMPILE_TEST
+ depends on QCOM_SMEM
select QCOM_SCM
select QCOM_QMI_HELPERS
help
diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c
index e76aab973320..438b0caaceb7 100644
--- a/drivers/net/wireless/ath/ath10k/htt_rx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
@@ -1379,7 +1379,7 @@ static void ath10k_process_rx(struct ath10k *ar, struct sk_buff *skb)
ath10k_get_tid(hdr, tid, sizeof(tid)),
is_multicast_ether_addr(ieee80211_get_DA(hdr)) ?
"mcast" : "ucast",
- (__le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4,
+ IEEE80211_SEQ_TO_SN(__le16_to_cpu(hdr->seq_ctrl)),
(status->encoding == RX_ENC_LEGACY) ? "legacy" : "",
(status->encoding == RX_ENC_HT) ? "ht" : "",
(status->encoding == RX_ENC_VHT) ? "vht" : "",
@@ -1844,15 +1844,14 @@ static void ath10k_htt_rx_h_csum_offload(struct ath10k_hw_params *hw,
}
static u64 ath10k_htt_rx_h_get_pn(struct ath10k *ar, struct sk_buff *skb,
- u16 offset,
enum htt_rx_mpdu_encrypt_type enctype)
{
struct ieee80211_hdr *hdr;
u64 pn = 0;
u8 *ehdr;
- hdr = (struct ieee80211_hdr *)(skb->data + offset);
- ehdr = skb->data + offset + ieee80211_hdrlen(hdr->frame_control);
+ hdr = (struct ieee80211_hdr *)skb->data;
+ ehdr = skb->data + ieee80211_hdrlen(hdr->frame_control);
if (enctype == HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2) {
pn = ehdr[0];
@@ -1866,19 +1865,17 @@ static u64 ath10k_htt_rx_h_get_pn(struct ath10k *ar, struct sk_buff *skb,
}
static bool ath10k_htt_rx_h_frag_multicast_check(struct ath10k *ar,
- struct sk_buff *skb,
- u16 offset)
+ struct sk_buff *skb)
{
struct ieee80211_hdr *hdr;
- hdr = (struct ieee80211_hdr *)(skb->data + offset);
+ hdr = (struct ieee80211_hdr *)skb->data;
return !is_multicast_ether_addr(hdr->addr1);
}
static bool ath10k_htt_rx_h_frag_pn_check(struct ath10k *ar,
struct sk_buff *skb,
u16 peer_id,
- u16 offset,
enum htt_rx_mpdu_encrypt_type enctype)
{
struct ath10k_peer *peer;
@@ -1893,16 +1890,16 @@ static bool ath10k_htt_rx_h_frag_pn_check(struct ath10k *ar,
return false;
}
- hdr = (struct ieee80211_hdr *)(skb->data + offset);
+ hdr = (struct ieee80211_hdr *)skb->data;
if (ieee80211_is_data_qos(hdr->frame_control))
tid = ieee80211_get_tid(hdr);
else
tid = ATH10K_TXRX_NON_QOS_TID;
last_pn = &peer->frag_tids_last_pn[tid];
- new_pn.pn48 = ath10k_htt_rx_h_get_pn(ar, skb, offset, enctype);
+ new_pn.pn48 = ath10k_htt_rx_h_get_pn(ar, skb, enctype);
frag_number = le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG;
- seq = (__le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4;
+ seq = IEEE80211_SEQ_TO_SN(__le16_to_cpu(hdr->seq_ctrl));
if (frag_number == 0) {
last_pn->pn48 = new_pn.pn48;
@@ -2059,13 +2056,11 @@ static void ath10k_htt_rx_h_mpdu(struct ath10k *ar,
frag_pn_check = ath10k_htt_rx_h_frag_pn_check(ar,
msdu,
peer_id,
- 0,
enctype);
if (frag)
multicast_check = ath10k_htt_rx_h_frag_multicast_check(ar,
- msdu,
- 0);
+ msdu);
if (!frag_pn_check || !multicast_check) {
/* Discard the fragment with invalid PN or multicast DA
@@ -2824,7 +2819,7 @@ static bool ath10k_htt_rx_proc_rx_frag_ind_hl(struct ath10k_htt *htt,
hdr_space = ieee80211_hdrlen(hdr->frame_control);
sc = __le16_to_cpu(hdr->seq_ctrl);
- seq = (sc & IEEE80211_SCTL_SEQ) >> 4;
+ seq = IEEE80211_SEQ_TO_SN(sc);
frag = sc & IEEE80211_SCTL_FRAG;
sec_index = MS(rx_desc_info, HTT_RX_DESC_HL_INFO_MCAST_BCAST) ?
diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c
index e56c6a6b1379..728d607289c3 100644
--- a/drivers/net/wireless/ath/ath10k/pci.c
+++ b/drivers/net/wireless/ath/ath10k/pci.c
@@ -3792,18 +3792,22 @@ static struct pci_driver ath10k_pci_driver = {
static int __init ath10k_pci_init(void)
{
- int ret;
+ int ret1, ret2;
- ret = pci_register_driver(&ath10k_pci_driver);
- if (ret)
+ ret1 = pci_register_driver(&ath10k_pci_driver);
+ if (ret1)
printk(KERN_ERR "failed to register ath10k pci driver: %d\n",
- ret);
+ ret1);
- ret = ath10k_ahb_init();
- if (ret)
- printk(KERN_ERR "ahb init failed: %d\n", ret);
+ ret2 = ath10k_ahb_init();
+ if (ret2)
+ printk(KERN_ERR "ahb init failed: %d\n", ret2);
- return ret;
+ if (ret1 && ret2)
+ return ret1;
+
+ /* registered to at least one bus */
+ return 0;
}
module_init(ath10k_pci_init);
diff --git a/drivers/net/wireless/ath/ath10k/qmi.c b/drivers/net/wireless/ath/ath10k/qmi.c
index 66cb7a1e628a..3f94fbf83702 100644
--- a/drivers/net/wireless/ath/ath10k/qmi.c
+++ b/drivers/net/wireless/ath/ath10k/qmi.c
@@ -14,6 +14,7 @@
#include <linux/net.h>
#include <linux/platform_device.h>
#include <linux/qcom_scm.h>
+#include <linux/soc/qcom/smem.h>
#include <linux/string.h>
#include <net/sock.h>
@@ -22,6 +23,10 @@
#define ATH10K_QMI_CLIENT_ID 0x4b4e454c
#define ATH10K_QMI_TIMEOUT 30
+#define SMEM_IMAGE_VERSION_TABLE 469
+#define SMEM_IMAGE_TABLE_CNSS_INDEX 13
+#define SMEM_IMAGE_VERSION_ENTRY_SIZE 128
+#define SMEM_IMAGE_VERSION_NAME_SIZE 75
static int ath10k_qmi_map_msa_permission(struct ath10k_qmi *qmi,
struct ath10k_msa_mem_info *mem_info)
@@ -536,6 +541,33 @@ int ath10k_qmi_wlan_disable(struct ath10k *ar)
return ath10k_qmi_mode_send_sync_msg(ar, QMI_WLFW_OFF_V01);
}
+static void ath10k_qmi_add_wlan_ver_smem(struct ath10k *ar, const char *fw_build_id)
+{
+ u8 *table_ptr;
+ size_t smem_item_size;
+ const u32 smem_img_idx_wlan = SMEM_IMAGE_TABLE_CNSS_INDEX *
+ SMEM_IMAGE_VERSION_ENTRY_SIZE;
+
+ table_ptr = qcom_smem_get(QCOM_SMEM_HOST_ANY,
+ SMEM_IMAGE_VERSION_TABLE,
+ &smem_item_size);
+
+ if (IS_ERR(table_ptr)) {
+ ath10k_err(ar, "smem image version table not found\n");
+ return;
+ }
+
+ if (smem_img_idx_wlan + SMEM_IMAGE_VERSION_ENTRY_SIZE >
+ smem_item_size) {
+ ath10k_err(ar, "smem block size too small: %zu\n",
+ smem_item_size);
+ return;
+ }
+
+ strscpy(table_ptr + smem_img_idx_wlan, fw_build_id,
+ SMEM_IMAGE_VERSION_NAME_SIZE);
+}
+
static int ath10k_qmi_cap_send_sync_msg(struct ath10k_qmi *qmi)
{
struct wlfw_cap_resp_msg_v01 *resp;
@@ -606,6 +638,9 @@ static int ath10k_qmi_cap_send_sync_msg(struct ath10k_qmi *qmi)
qmi->fw_version, qmi->fw_build_timestamp, qmi->fw_build_id);
}
+ if (resp->fw_build_id_valid)
+ ath10k_qmi_add_wlan_ver_smem(ar, qmi->fw_build_id);
+
kfree(resp);
return 0;
@@ -618,7 +653,7 @@ static int ath10k_qmi_host_cap_send_sync(struct ath10k_qmi *qmi)
{
struct wlfw_host_cap_resp_msg_v01 resp = {};
struct wlfw_host_cap_req_msg_v01 req = {};
- struct qmi_elem_info *req_ei;
+ const struct qmi_elem_info *req_ei;
struct ath10k *ar = qmi->ar;
struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
struct qmi_txn txn;
diff --git a/drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.c b/drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.c
index 86fcf4e1de5f..1c81e454f943 100644
--- a/drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.c
+++ b/drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.c
@@ -7,7 +7,7 @@
#include <linux/types.h>
#include "qmi_wlfw_v01.h"
-static struct qmi_elem_info wlfw_ce_tgt_pipe_cfg_s_v01_ei[] = {
+static const struct qmi_elem_info wlfw_ce_tgt_pipe_cfg_s_v01_ei[] = {
{
.data_type = QMI_UNSIGNED_4_BYTE,
.elem_len = 1,
@@ -56,7 +56,7 @@ static struct qmi_elem_info wlfw_ce_tgt_pipe_cfg_s_v01_ei[] = {
{}
};
-static struct qmi_elem_info wlfw_ce_svc_pipe_cfg_s_v01_ei[] = {
+static const struct qmi_elem_info wlfw_ce_svc_pipe_cfg_s_v01_ei[] = {
{
.data_type = QMI_UNSIGNED_4_BYTE,
.elem_len = 1,
@@ -87,7 +87,7 @@ static struct qmi_elem_info wlfw_ce_svc_pipe_cfg_s_v01_ei[] = {
{}
};
-static struct qmi_elem_info wlfw_shadow_reg_cfg_s_v01_ei[] = {
+static const struct qmi_elem_info wlfw_shadow_reg_cfg_s_v01_ei[] = {
{
.data_type = QMI_UNSIGNED_2_BYTE,
.elem_len = 1,
@@ -109,7 +109,7 @@ static struct qmi_elem_info wlfw_shadow_reg_cfg_s_v01_ei[] = {
{}
};
-static struct qmi_elem_info wlfw_shadow_reg_v2_cfg_s_v01_ei[] = {
+static const struct qmi_elem_info wlfw_shadow_reg_v2_cfg_s_v01_ei[] = {
{
.data_type = QMI_UNSIGNED_4_BYTE,
.elem_len = 1,
@@ -122,7 +122,7 @@ static struct qmi_elem_info wlfw_shadow_reg_v2_cfg_s_v01_ei[] = {
{}
};
-static struct qmi_elem_info wlfw_memory_region_info_s_v01_ei[] = {
+static const struct qmi_elem_info wlfw_memory_region_info_s_v01_ei[] = {
{
.data_type = QMI_UNSIGNED_8_BYTE,
.elem_len = 1,
@@ -153,7 +153,7 @@ static struct qmi_elem_info wlfw_memory_region_info_s_v01_ei[] = {
{}
};
-static struct qmi_elem_info wlfw_mem_cfg_s_v01_ei[] = {
+static const struct qmi_elem_info wlfw_mem_cfg_s_v01_ei[] = {
{
.data_type = QMI_UNSIGNED_8_BYTE,
.elem_len = 1,
@@ -184,7 +184,7 @@ static struct qmi_elem_info wlfw_mem_cfg_s_v01_ei[] = {
{}
};
-static struct qmi_elem_info wlfw_mem_seg_s_v01_ei[] = {
+static const struct qmi_elem_info wlfw_mem_seg_s_v01_ei[] = {
{
.data_type = QMI_UNSIGNED_4_BYTE,
.elem_len = 1,
@@ -225,7 +225,7 @@ static struct qmi_elem_info wlfw_mem_seg_s_v01_ei[] = {
{}
};
-static struct qmi_elem_info wlfw_mem_seg_resp_s_v01_ei[] = {
+static const struct qmi_elem_info wlfw_mem_seg_resp_s_v01_ei[] = {
{
.data_type = QMI_UNSIGNED_8_BYTE,
.elem_len = 1,
@@ -256,7 +256,7 @@ static struct qmi_elem_info wlfw_mem_seg_resp_s_v01_ei[] = {
{}
};
-static struct qmi_elem_info wlfw_rf_chip_info_s_v01_ei[] = {
+static const struct qmi_elem_info wlfw_rf_chip_info_s_v01_ei[] = {
{
.data_type = QMI_UNSIGNED_4_BYTE,
.elem_len = 1,
@@ -278,7 +278,7 @@ static struct qmi_elem_info wlfw_rf_chip_info_s_v01_ei[] = {
{}
};
-static struct qmi_elem_info wlfw_rf_board_info_s_v01_ei[] = {
+static const struct qmi_elem_info wlfw_rf_board_info_s_v01_ei[] = {
{
.data_type = QMI_UNSIGNED_4_BYTE,
.elem_len = 1,
@@ -291,7 +291,7 @@ static struct qmi_elem_info wlfw_rf_board_info_s_v01_ei[] = {
{}
};
-static struct qmi_elem_info wlfw_soc_info_s_v01_ei[] = {
+static const struct qmi_elem_info wlfw_soc_info_s_v01_ei[] = {
{
.data_type = QMI_UNSIGNED_4_BYTE,
.elem_len = 1,
@@ -304,7 +304,7 @@ static struct qmi_elem_info wlfw_soc_info_s_v01_ei[] = {
{}
};
-static struct qmi_elem_info wlfw_fw_version_info_s_v01_ei[] = {
+static const struct qmi_elem_info wlfw_fw_version_info_s_v01_ei[] = {
{
.data_type = QMI_UNSIGNED_4_BYTE,
.elem_len = 1,
@@ -326,7 +326,7 @@ static struct qmi_elem_info wlfw_fw_version_info_s_v01_ei[] = {
{}
};
-struct qmi_elem_info wlfw_ind_register_req_msg_v01_ei[] = {
+const struct qmi_elem_info wlfw_ind_register_req_msg_v01_ei[] = {
{
.data_type = QMI_OPT_FLAG,
.elem_len = 1,
@@ -528,7 +528,7 @@ struct qmi_elem_info wlfw_ind_register_req_msg_v01_ei[] = {
{}
};
-struct qmi_elem_info wlfw_ind_register_resp_msg_v01_ei[] = {
+const struct qmi_elem_info wlfw_ind_register_resp_msg_v01_ei[] = {
{
.data_type = QMI_STRUCT,
.elem_len = 1,
@@ -560,15 +560,15 @@ struct qmi_elem_info wlfw_ind_register_resp_msg_v01_ei[] = {
{}
};
-struct qmi_elem_info wlfw_fw_ready_ind_msg_v01_ei[] = {
+const struct qmi_elem_info wlfw_fw_ready_ind_msg_v01_ei[] = {
{}
};
-struct qmi_elem_info wlfw_msa_ready_ind_msg_v01_ei[] = {
+const struct qmi_elem_info wlfw_msa_ready_ind_msg_v01_ei[] = {
{}
};
-struct qmi_elem_info wlfw_pin_connect_result_ind_msg_v01_ei[] = {
+const struct qmi_elem_info wlfw_pin_connect_result_ind_msg_v01_ei[] = {
{
.data_type = QMI_OPT_FLAG,
.elem_len = 1,
@@ -626,7 +626,7 @@ struct qmi_elem_info wlfw_pin_connect_result_ind_msg_v01_ei[] = {
{}
};
-struct qmi_elem_info wlfw_wlan_mode_req_msg_v01_ei[] = {
+const struct qmi_elem_info wlfw_wlan_mode_req_msg_v01_ei[] = {
{
.data_type = QMI_SIGNED_4_BYTE_ENUM,
.elem_len = 1,
@@ -657,7 +657,7 @@ struct qmi_elem_info wlfw_wlan_mode_req_msg_v01_ei[] = {
{}
};
-struct qmi_elem_info wlfw_wlan_mode_resp_msg_v01_ei[] = {
+const struct qmi_elem_info wlfw_wlan_mode_resp_msg_v01_ei[] = {
{
.data_type = QMI_STRUCT,
.elem_len = 1,
@@ -671,7 +671,7 @@ struct qmi_elem_info wlfw_wlan_mode_resp_msg_v01_ei[] = {
{}
};
-struct qmi_elem_info wlfw_wlan_cfg_req_msg_v01_ei[] = {
+const struct qmi_elem_info wlfw_wlan_cfg_req_msg_v01_ei[] = {
{
.data_type = QMI_OPT_FLAG,
.elem_len = 1,
@@ -805,7 +805,7 @@ struct qmi_elem_info wlfw_wlan_cfg_req_msg_v01_ei[] = {
{}
};
-struct qmi_elem_info wlfw_wlan_cfg_resp_msg_v01_ei[] = {
+const struct qmi_elem_info wlfw_wlan_cfg_resp_msg_v01_ei[] = {
{
.data_type = QMI_STRUCT,
.elem_len = 1,
@@ -819,11 +819,11 @@ struct qmi_elem_info wlfw_wlan_cfg_resp_msg_v01_ei[] = {
{}
};
-struct qmi_elem_info wlfw_cap_req_msg_v01_ei[] = {
+const struct qmi_elem_info wlfw_cap_req_msg_v01_ei[] = {
{}
};
-struct qmi_elem_info wlfw_cap_resp_msg_v01_ei[] = {
+const struct qmi_elem_info wlfw_cap_resp_msg_v01_ei[] = {
{
.data_type = QMI_STRUCT,
.elem_len = 1,
@@ -949,7 +949,7 @@ struct qmi_elem_info wlfw_cap_resp_msg_v01_ei[] = {
{}
};
-struct qmi_elem_info wlfw_bdf_download_req_msg_v01_ei[] = {
+const struct qmi_elem_info wlfw_bdf_download_req_msg_v01_ei[] = {
{
.data_type = QMI_UNSIGNED_1_BYTE,
.elem_len = 1,
@@ -1079,7 +1079,7 @@ struct qmi_elem_info wlfw_bdf_download_req_msg_v01_ei[] = {
{}
};
-struct qmi_elem_info wlfw_bdf_download_resp_msg_v01_ei[] = {
+const struct qmi_elem_info wlfw_bdf_download_resp_msg_v01_ei[] = {
{
.data_type = QMI_STRUCT,
.elem_len = 1,
@@ -1093,7 +1093,7 @@ struct qmi_elem_info wlfw_bdf_download_resp_msg_v01_ei[] = {
{}
};
-struct qmi_elem_info wlfw_cal_report_req_msg_v01_ei[] = {
+const struct qmi_elem_info wlfw_cal_report_req_msg_v01_ei[] = {
{
.data_type = QMI_DATA_LEN,
.elem_len = 1,
@@ -1133,7 +1133,7 @@ struct qmi_elem_info wlfw_cal_report_req_msg_v01_ei[] = {
{}
};
-struct qmi_elem_info wlfw_cal_report_resp_msg_v01_ei[] = {
+const struct qmi_elem_info wlfw_cal_report_resp_msg_v01_ei[] = {
{
.data_type = QMI_STRUCT,
.elem_len = 1,
@@ -1147,7 +1147,7 @@ struct qmi_elem_info wlfw_cal_report_resp_msg_v01_ei[] = {
{}
};
-struct qmi_elem_info wlfw_initiate_cal_download_ind_msg_v01_ei[] = {
+const struct qmi_elem_info wlfw_initiate_cal_download_ind_msg_v01_ei[] = {
{
.data_type = QMI_SIGNED_4_BYTE_ENUM,
.elem_len = 1,
@@ -1160,7 +1160,7 @@ struct qmi_elem_info wlfw_initiate_cal_download_ind_msg_v01_ei[] = {
{}
};
-struct qmi_elem_info wlfw_cal_download_req_msg_v01_ei[] = {
+const struct qmi_elem_info wlfw_cal_download_req_msg_v01_ei[] = {
{
.data_type = QMI_UNSIGNED_1_BYTE,
.elem_len = 1,
@@ -1272,7 +1272,7 @@ struct qmi_elem_info wlfw_cal_download_req_msg_v01_ei[] = {
{}
};
-struct qmi_elem_info wlfw_cal_download_resp_msg_v01_ei[] = {
+const struct qmi_elem_info wlfw_cal_download_resp_msg_v01_ei[] = {
{
.data_type = QMI_STRUCT,
.elem_len = 1,
@@ -1286,7 +1286,7 @@ struct qmi_elem_info wlfw_cal_download_resp_msg_v01_ei[] = {
{}
};
-struct qmi_elem_info wlfw_initiate_cal_update_ind_msg_v01_ei[] = {
+const struct qmi_elem_info wlfw_initiate_cal_update_ind_msg_v01_ei[] = {
{
.data_type = QMI_SIGNED_4_BYTE_ENUM,
.elem_len = 1,
@@ -1308,7 +1308,7 @@ struct qmi_elem_info wlfw_initiate_cal_update_ind_msg_v01_ei[] = {
{}
};
-struct qmi_elem_info wlfw_cal_update_req_msg_v01_ei[] = {
+const struct qmi_elem_info wlfw_cal_update_req_msg_v01_ei[] = {
{
.data_type = QMI_SIGNED_4_BYTE_ENUM,
.elem_len = 1,
@@ -1330,7 +1330,7 @@ struct qmi_elem_info wlfw_cal_update_req_msg_v01_ei[] = {
{}
};
-struct qmi_elem_info wlfw_cal_update_resp_msg_v01_ei[] = {
+const struct qmi_elem_info wlfw_cal_update_resp_msg_v01_ei[] = {
{
.data_type = QMI_STRUCT,
.elem_len = 1,
@@ -1443,7 +1443,7 @@ struct qmi_elem_info wlfw_cal_update_resp_msg_v01_ei[] = {
{}
};
-struct qmi_elem_info wlfw_msa_info_req_msg_v01_ei[] = {
+const struct qmi_elem_info wlfw_msa_info_req_msg_v01_ei[] = {
{
.data_type = QMI_UNSIGNED_8_BYTE,
.elem_len = 1,
@@ -1465,7 +1465,7 @@ struct qmi_elem_info wlfw_msa_info_req_msg_v01_ei[] = {
{}
};
-struct qmi_elem_info wlfw_msa_info_resp_msg_v01_ei[] = {
+const struct qmi_elem_info wlfw_msa_info_resp_msg_v01_ei[] = {
{
.data_type = QMI_STRUCT,
.elem_len = 1,
@@ -1498,11 +1498,11 @@ struct qmi_elem_info wlfw_msa_info_resp_msg_v01_ei[] = {
{}
};
-struct qmi_elem_info wlfw_msa_ready_req_msg_v01_ei[] = {
+const struct qmi_elem_info wlfw_msa_ready_req_msg_v01_ei[] = {
{}
};
-struct qmi_elem_info wlfw_msa_ready_resp_msg_v01_ei[] = {
+const struct qmi_elem_info wlfw_msa_ready_resp_msg_v01_ei[] = {
{
.data_type = QMI_STRUCT,
.elem_len = 1,
@@ -1516,7 +1516,7 @@ struct qmi_elem_info wlfw_msa_ready_resp_msg_v01_ei[] = {
{}
};
-struct qmi_elem_info wlfw_ini_req_msg_v01_ei[] = {
+const struct qmi_elem_info wlfw_ini_req_msg_v01_ei[] = {
{
.data_type = QMI_OPT_FLAG,
.elem_len = 1,
@@ -1538,7 +1538,7 @@ struct qmi_elem_info wlfw_ini_req_msg_v01_ei[] = {
{}
};
-struct qmi_elem_info wlfw_ini_resp_msg_v01_ei[] = {
+const struct qmi_elem_info wlfw_ini_resp_msg_v01_ei[] = {
{
.data_type = QMI_STRUCT,
.elem_len = 1,
@@ -1552,7 +1552,7 @@ struct qmi_elem_info wlfw_ini_resp_msg_v01_ei[] = {
{}
};
-struct qmi_elem_info wlfw_athdiag_read_req_msg_v01_ei[] = {
+const struct qmi_elem_info wlfw_athdiag_read_req_msg_v01_ei[] = {
{
.data_type = QMI_UNSIGNED_4_BYTE,
.elem_len = 1,
@@ -1583,7 +1583,7 @@ struct qmi_elem_info wlfw_athdiag_read_req_msg_v01_ei[] = {
{}
};
-struct qmi_elem_info wlfw_athdiag_read_resp_msg_v01_ei[] = {
+const struct qmi_elem_info wlfw_athdiag_read_resp_msg_v01_ei[] = {
{
.data_type = QMI_STRUCT,
.elem_len = 1,
@@ -1624,7 +1624,7 @@ struct qmi_elem_info wlfw_athdiag_read_resp_msg_v01_ei[] = {
{}
};
-struct qmi_elem_info wlfw_athdiag_write_req_msg_v01_ei[] = {
+const struct qmi_elem_info wlfw_athdiag_write_req_msg_v01_ei[] = {
{
.data_type = QMI_UNSIGNED_4_BYTE,
.elem_len = 1,
@@ -1664,7 +1664,7 @@ struct qmi_elem_info wlfw_athdiag_write_req_msg_v01_ei[] = {
{}
};
-struct qmi_elem_info wlfw_athdiag_write_resp_msg_v01_ei[] = {
+const struct qmi_elem_info wlfw_athdiag_write_resp_msg_v01_ei[] = {
{
.data_type = QMI_STRUCT,
.elem_len = 1,
@@ -1678,7 +1678,7 @@ struct qmi_elem_info wlfw_athdiag_write_resp_msg_v01_ei[] = {
{}
};
-struct qmi_elem_info wlfw_vbatt_req_msg_v01_ei[] = {
+const struct qmi_elem_info wlfw_vbatt_req_msg_v01_ei[] = {
{
.data_type = QMI_UNSIGNED_8_BYTE,
.elem_len = 1,
@@ -1691,7 +1691,7 @@ struct qmi_elem_info wlfw_vbatt_req_msg_v01_ei[] = {
{}
};
-struct qmi_elem_info wlfw_vbatt_resp_msg_v01_ei[] = {
+const struct qmi_elem_info wlfw_vbatt_resp_msg_v01_ei[] = {
{
.data_type = QMI_STRUCT,
.elem_len = 1,
@@ -1705,7 +1705,7 @@ struct qmi_elem_info wlfw_vbatt_resp_msg_v01_ei[] = {
{}
};
-struct qmi_elem_info wlfw_mac_addr_req_msg_v01_ei[] = {
+const struct qmi_elem_info wlfw_mac_addr_req_msg_v01_ei[] = {
{
.data_type = QMI_OPT_FLAG,
.elem_len = 1,
@@ -1727,7 +1727,7 @@ struct qmi_elem_info wlfw_mac_addr_req_msg_v01_ei[] = {
{}
};
-struct qmi_elem_info wlfw_mac_addr_resp_msg_v01_ei[] = {
+const struct qmi_elem_info wlfw_mac_addr_resp_msg_v01_ei[] = {
{
.data_type = QMI_STRUCT,
.elem_len = 1,
@@ -1741,7 +1741,7 @@ struct qmi_elem_info wlfw_mac_addr_resp_msg_v01_ei[] = {
{}
};
-struct qmi_elem_info wlfw_host_cap_req_msg_v01_ei[] = {
+const struct qmi_elem_info wlfw_host_cap_req_msg_v01_ei[] = {
{
.data_type = QMI_OPT_FLAG,
.elem_len = 1,
@@ -1988,7 +1988,7 @@ struct qmi_elem_info wlfw_host_cap_req_msg_v01_ei[] = {
{}
};
-struct qmi_elem_info wlfw_host_cap_8bit_req_msg_v01_ei[] = {
+const struct qmi_elem_info wlfw_host_cap_8bit_req_msg_v01_ei[] = {
{
.data_type = QMI_OPT_FLAG,
.elem_len = 1,
@@ -2010,7 +2010,7 @@ struct qmi_elem_info wlfw_host_cap_8bit_req_msg_v01_ei[] = {
{}
};
-struct qmi_elem_info wlfw_host_cap_resp_msg_v01_ei[] = {
+const struct qmi_elem_info wlfw_host_cap_resp_msg_v01_ei[] = {
{
.data_type = QMI_STRUCT,
.elem_len = 1,
@@ -2024,7 +2024,7 @@ struct qmi_elem_info wlfw_host_cap_resp_msg_v01_ei[] = {
{}
};
-struct qmi_elem_info wlfw_request_mem_ind_msg_v01_ei[] = {
+const struct qmi_elem_info wlfw_request_mem_ind_msg_v01_ei[] = {
{
.data_type = QMI_DATA_LEN,
.elem_len = 1,
@@ -2047,7 +2047,7 @@ struct qmi_elem_info wlfw_request_mem_ind_msg_v01_ei[] = {
{}
};
-struct qmi_elem_info wlfw_respond_mem_req_msg_v01_ei[] = {
+const struct qmi_elem_info wlfw_respond_mem_req_msg_v01_ei[] = {
{
.data_type = QMI_DATA_LEN,
.elem_len = 1,
@@ -2070,7 +2070,7 @@ struct qmi_elem_info wlfw_respond_mem_req_msg_v01_ei[] = {
{}
};
-struct qmi_elem_info wlfw_respond_mem_resp_msg_v01_ei[] = {
+const struct qmi_elem_info wlfw_respond_mem_resp_msg_v01_ei[] = {
{
.data_type = QMI_STRUCT,
.elem_len = 1,
@@ -2084,15 +2084,15 @@ struct qmi_elem_info wlfw_respond_mem_resp_msg_v01_ei[] = {
{}
};
-struct qmi_elem_info wlfw_mem_ready_ind_msg_v01_ei[] = {
+const struct qmi_elem_info wlfw_mem_ready_ind_msg_v01_ei[] = {
{}
};
-struct qmi_elem_info wlfw_fw_init_done_ind_msg_v01_ei[] = {
+const struct qmi_elem_info wlfw_fw_init_done_ind_msg_v01_ei[] = {
{}
};
-struct qmi_elem_info wlfw_rejuvenate_ind_msg_v01_ei[] = {
+const struct qmi_elem_info wlfw_rejuvenate_ind_msg_v01_ei[] = {
{
.data_type = QMI_OPT_FLAG,
.elem_len = 1,
@@ -2168,11 +2168,11 @@ struct qmi_elem_info wlfw_rejuvenate_ind_msg_v01_ei[] = {
{}
};
-struct qmi_elem_info wlfw_rejuvenate_ack_req_msg_v01_ei[] = {
+const struct qmi_elem_info wlfw_rejuvenate_ack_req_msg_v01_ei[] = {
{}
};
-struct qmi_elem_info wlfw_rejuvenate_ack_resp_msg_v01_ei[] = {
+const struct qmi_elem_info wlfw_rejuvenate_ack_resp_msg_v01_ei[] = {
{
.data_type = QMI_STRUCT,
.elem_len = 1,
@@ -2186,7 +2186,7 @@ struct qmi_elem_info wlfw_rejuvenate_ack_resp_msg_v01_ei[] = {
{}
};
-struct qmi_elem_info wlfw_dynamic_feature_mask_req_msg_v01_ei[] = {
+const struct qmi_elem_info wlfw_dynamic_feature_mask_req_msg_v01_ei[] = {
{
.data_type = QMI_OPT_FLAG,
.elem_len = 1,
@@ -2208,7 +2208,7 @@ struct qmi_elem_info wlfw_dynamic_feature_mask_req_msg_v01_ei[] = {
{}
};
-struct qmi_elem_info wlfw_dynamic_feature_mask_resp_msg_v01_ei[] = {
+const struct qmi_elem_info wlfw_dynamic_feature_mask_resp_msg_v01_ei[] = {
{
.data_type = QMI_STRUCT,
.elem_len = 1,
@@ -2258,7 +2258,7 @@ struct qmi_elem_info wlfw_dynamic_feature_mask_resp_msg_v01_ei[] = {
{}
};
-struct qmi_elem_info wlfw_m3_info_req_msg_v01_ei[] = {
+const struct qmi_elem_info wlfw_m3_info_req_msg_v01_ei[] = {
{
.data_type = QMI_UNSIGNED_8_BYTE,
.elem_len = 1,
@@ -2280,7 +2280,7 @@ struct qmi_elem_info wlfw_m3_info_req_msg_v01_ei[] = {
{}
};
-struct qmi_elem_info wlfw_m3_info_resp_msg_v01_ei[] = {
+const struct qmi_elem_info wlfw_m3_info_resp_msg_v01_ei[] = {
{
.data_type = QMI_STRUCT,
.elem_len = 1,
@@ -2294,7 +2294,7 @@ struct qmi_elem_info wlfw_m3_info_resp_msg_v01_ei[] = {
{}
};
-struct qmi_elem_info wlfw_xo_cal_ind_msg_v01_ei[] = {
+const struct qmi_elem_info wlfw_xo_cal_ind_msg_v01_ei[] = {
{
.data_type = QMI_UNSIGNED_1_BYTE,
.elem_len = 1,
diff --git a/drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.h b/drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.h
index 4d107e1364a8..f0db991408dc 100644
--- a/drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.h
+++ b/drivers/net/wireless/ath/ath10k/qmi_wlfw_v01.h
@@ -215,7 +215,7 @@ struct wlfw_ind_register_req_msg_v01 {
};
#define WLFW_IND_REGISTER_REQ_MSG_V01_MAX_MSG_LEN 50
-extern struct qmi_elem_info wlfw_ind_register_req_msg_v01_ei[];
+extern const struct qmi_elem_info wlfw_ind_register_req_msg_v01_ei[];
struct wlfw_ind_register_resp_msg_v01 {
struct qmi_response_type_v01 resp;
@@ -224,21 +224,21 @@ struct wlfw_ind_register_resp_msg_v01 {
};
#define WLFW_IND_REGISTER_RESP_MSG_V01_MAX_MSG_LEN 18
-extern struct qmi_elem_info wlfw_ind_register_resp_msg_v01_ei[];
+extern const struct qmi_elem_info wlfw_ind_register_resp_msg_v01_ei[];
struct wlfw_fw_ready_ind_msg_v01 {
char placeholder;
};
#define WLFW_FW_READY_IND_MSG_V01_MAX_MSG_LEN 0
-extern struct qmi_elem_info wlfw_fw_ready_ind_msg_v01_ei[];
+extern const struct qmi_elem_info wlfw_fw_ready_ind_msg_v01_ei[];
struct wlfw_msa_ready_ind_msg_v01 {
char placeholder;
};
#define WLFW_MSA_READY_IND_MSG_V01_MAX_MSG_LEN 0
-extern struct qmi_elem_info wlfw_msa_ready_ind_msg_v01_ei[];
+extern const struct qmi_elem_info wlfw_msa_ready_ind_msg_v01_ei[];
struct wlfw_pin_connect_result_ind_msg_v01 {
u8 pwr_pin_result_valid;
@@ -250,7 +250,7 @@ struct wlfw_pin_connect_result_ind_msg_v01 {
};
#define WLFW_PIN_CONNECT_RESULT_IND_MSG_V01_MAX_MSG_LEN 21
-extern struct qmi_elem_info wlfw_pin_connect_result_ind_msg_v01_ei[];
+extern const struct qmi_elem_info wlfw_pin_connect_result_ind_msg_v01_ei[];
struct wlfw_wlan_mode_req_msg_v01 {
enum wlfw_driver_mode_enum_v01 mode;
@@ -259,14 +259,14 @@ struct wlfw_wlan_mode_req_msg_v01 {
};
#define WLFW_WLAN_MODE_REQ_MSG_V01_MAX_MSG_LEN 11
-extern struct qmi_elem_info wlfw_wlan_mode_req_msg_v01_ei[];
+extern const struct qmi_elem_info wlfw_wlan_mode_req_msg_v01_ei[];
struct wlfw_wlan_mode_resp_msg_v01 {
struct qmi_response_type_v01 resp;
};
#define WLFW_WLAN_MODE_RESP_MSG_V01_MAX_MSG_LEN 7
-extern struct qmi_elem_info wlfw_wlan_mode_resp_msg_v01_ei[];
+extern const struct qmi_elem_info wlfw_wlan_mode_resp_msg_v01_ei[];
struct wlfw_wlan_cfg_req_msg_v01 {
u8 host_version_valid;
@@ -286,21 +286,21 @@ struct wlfw_wlan_cfg_req_msg_v01 {
};
#define WLFW_WLAN_CFG_REQ_MSG_V01_MAX_MSG_LEN 803
-extern struct qmi_elem_info wlfw_wlan_cfg_req_msg_v01_ei[];
+extern const struct qmi_elem_info wlfw_wlan_cfg_req_msg_v01_ei[];
struct wlfw_wlan_cfg_resp_msg_v01 {
struct qmi_response_type_v01 resp;
};
#define WLFW_WLAN_CFG_RESP_MSG_V01_MAX_MSG_LEN 7
-extern struct qmi_elem_info wlfw_wlan_cfg_resp_msg_v01_ei[];
+extern const struct qmi_elem_info wlfw_wlan_cfg_resp_msg_v01_ei[];
struct wlfw_cap_req_msg_v01 {
char placeholder;
};
#define WLFW_CAP_REQ_MSG_V01_MAX_MSG_LEN 0
-extern struct qmi_elem_info wlfw_cap_req_msg_v01_ei[];
+extern const struct qmi_elem_info wlfw_cap_req_msg_v01_ei[];
struct wlfw_cap_resp_msg_v01 {
struct qmi_response_type_v01 resp;
@@ -319,7 +319,7 @@ struct wlfw_cap_resp_msg_v01 {
};
#define WLFW_CAP_RESP_MSG_V01_MAX_MSG_LEN 207
-extern struct qmi_elem_info wlfw_cap_resp_msg_v01_ei[];
+extern const struct qmi_elem_info wlfw_cap_resp_msg_v01_ei[];
struct wlfw_bdf_download_req_msg_v01 {
u8 valid;
@@ -339,14 +339,14 @@ struct wlfw_bdf_download_req_msg_v01 {
};
#define WLFW_BDF_DOWNLOAD_REQ_MSG_V01_MAX_MSG_LEN 6182
-extern struct qmi_elem_info wlfw_bdf_download_req_msg_v01_ei[];
+extern const struct qmi_elem_info wlfw_bdf_download_req_msg_v01_ei[];
struct wlfw_bdf_download_resp_msg_v01 {
struct qmi_response_type_v01 resp;
};
#define WLFW_BDF_DOWNLOAD_RESP_MSG_V01_MAX_MSG_LEN 7
-extern struct qmi_elem_info wlfw_bdf_download_resp_msg_v01_ei[];
+extern const struct qmi_elem_info wlfw_bdf_download_resp_msg_v01_ei[];
struct wlfw_cal_report_req_msg_v01 {
u32 meta_data_len;
@@ -356,21 +356,21 @@ struct wlfw_cal_report_req_msg_v01 {
};
#define WLFW_CAL_REPORT_REQ_MSG_V01_MAX_MSG_LEN 28
-extern struct qmi_elem_info wlfw_cal_report_req_msg_v01_ei[];
+extern const struct qmi_elem_info wlfw_cal_report_req_msg_v01_ei[];
struct wlfw_cal_report_resp_msg_v01 {
struct qmi_response_type_v01 resp;
};
#define WLFW_CAL_REPORT_RESP_MSG_V01_MAX_MSG_LEN 7
-extern struct qmi_elem_info wlfw_cal_report_resp_msg_v01_ei[];
+extern const struct qmi_elem_info wlfw_cal_report_resp_msg_v01_ei[];
struct wlfw_initiate_cal_download_ind_msg_v01 {
enum wlfw_cal_temp_id_enum_v01 cal_id;
};
#define WLFW_INITIATE_CAL_DOWNLOAD_IND_MSG_V01_MAX_MSG_LEN 7
-extern struct qmi_elem_info wlfw_initiate_cal_download_ind_msg_v01_ei[];
+extern const struct qmi_elem_info wlfw_initiate_cal_download_ind_msg_v01_ei[];
struct wlfw_cal_download_req_msg_v01 {
u8 valid;
@@ -388,14 +388,14 @@ struct wlfw_cal_download_req_msg_v01 {
};
#define WLFW_CAL_DOWNLOAD_REQ_MSG_V01_MAX_MSG_LEN 6178
-extern struct qmi_elem_info wlfw_cal_download_req_msg_v01_ei[];
+extern const struct qmi_elem_info wlfw_cal_download_req_msg_v01_ei[];
struct wlfw_cal_download_resp_msg_v01 {
struct qmi_response_type_v01 resp;
};
#define WLFW_CAL_DOWNLOAD_RESP_MSG_V01_MAX_MSG_LEN 7
-extern struct qmi_elem_info wlfw_cal_download_resp_msg_v01_ei[];
+extern const struct qmi_elem_info wlfw_cal_download_resp_msg_v01_ei[];
struct wlfw_initiate_cal_update_ind_msg_v01 {
enum wlfw_cal_temp_id_enum_v01 cal_id;
@@ -403,7 +403,7 @@ struct wlfw_initiate_cal_update_ind_msg_v01 {
};
#define WLFW_INITIATE_CAL_UPDATE_IND_MSG_V01_MAX_MSG_LEN 14
-extern struct qmi_elem_info wlfw_initiate_cal_update_ind_msg_v01_ei[];
+extern const struct qmi_elem_info wlfw_initiate_cal_update_ind_msg_v01_ei[];
struct wlfw_cal_update_req_msg_v01 {
enum wlfw_cal_temp_id_enum_v01 cal_id;
@@ -411,7 +411,7 @@ struct wlfw_cal_update_req_msg_v01 {
};
#define WLFW_CAL_UPDATE_REQ_MSG_V01_MAX_MSG_LEN 14
-extern struct qmi_elem_info wlfw_cal_update_req_msg_v01_ei[];
+extern const struct qmi_elem_info wlfw_cal_update_req_msg_v01_ei[];
struct wlfw_cal_update_resp_msg_v01 {
struct qmi_response_type_v01 resp;
@@ -429,7 +429,7 @@ struct wlfw_cal_update_resp_msg_v01 {
};
#define WLFW_CAL_UPDATE_RESP_MSG_V01_MAX_MSG_LEN 6181
-extern struct qmi_elem_info wlfw_cal_update_resp_msg_v01_ei[];
+extern const struct qmi_elem_info wlfw_cal_update_resp_msg_v01_ei[];
struct wlfw_msa_info_req_msg_v01 {
u64 msa_addr;
@@ -437,7 +437,7 @@ struct wlfw_msa_info_req_msg_v01 {
};
#define WLFW_MSA_INFO_REQ_MSG_V01_MAX_MSG_LEN 18
-extern struct qmi_elem_info wlfw_msa_info_req_msg_v01_ei[];
+extern const struct qmi_elem_info wlfw_msa_info_req_msg_v01_ei[];
struct wlfw_msa_info_resp_msg_v01 {
struct qmi_response_type_v01 resp;
@@ -446,21 +446,21 @@ struct wlfw_msa_info_resp_msg_v01 {
};
#define WLFW_MSA_INFO_RESP_MSG_V01_MAX_MSG_LEN 37
-extern struct qmi_elem_info wlfw_msa_info_resp_msg_v01_ei[];
+extern const struct qmi_elem_info wlfw_msa_info_resp_msg_v01_ei[];
struct wlfw_msa_ready_req_msg_v01 {
char placeholder;
};
#define WLFW_MSA_READY_REQ_MSG_V01_MAX_MSG_LEN 0
-extern struct qmi_elem_info wlfw_msa_ready_req_msg_v01_ei[];
+extern const struct qmi_elem_info wlfw_msa_ready_req_msg_v01_ei[];
struct wlfw_msa_ready_resp_msg_v01 {
struct qmi_response_type_v01 resp;
};
#define WLFW_MSA_READY_RESP_MSG_V01_MAX_MSG_LEN 7
-extern struct qmi_elem_info wlfw_msa_ready_resp_msg_v01_ei[];
+extern const struct qmi_elem_info wlfw_msa_ready_resp_msg_v01_ei[];
struct wlfw_ini_req_msg_v01 {
u8 enablefwlog_valid;
@@ -468,14 +468,14 @@ struct wlfw_ini_req_msg_v01 {
};
#define WLFW_INI_REQ_MSG_V01_MAX_MSG_LEN 4
-extern struct qmi_elem_info wlfw_ini_req_msg_v01_ei[];
+extern const struct qmi_elem_info wlfw_ini_req_msg_v01_ei[];
struct wlfw_ini_resp_msg_v01 {
struct qmi_response_type_v01 resp;
};
#define WLFW_INI_RESP_MSG_V01_MAX_MSG_LEN 7
-extern struct qmi_elem_info wlfw_ini_resp_msg_v01_ei[];
+extern const struct qmi_elem_info wlfw_ini_resp_msg_v01_ei[];
struct wlfw_athdiag_read_req_msg_v01 {
u32 offset;
@@ -484,7 +484,7 @@ struct wlfw_athdiag_read_req_msg_v01 {
};
#define WLFW_ATHDIAG_READ_REQ_MSG_V01_MAX_MSG_LEN 21
-extern struct qmi_elem_info wlfw_athdiag_read_req_msg_v01_ei[];
+extern const struct qmi_elem_info wlfw_athdiag_read_req_msg_v01_ei[];
struct wlfw_athdiag_read_resp_msg_v01 {
struct qmi_response_type_v01 resp;
@@ -494,7 +494,7 @@ struct wlfw_athdiag_read_resp_msg_v01 {
};
#define WLFW_ATHDIAG_READ_RESP_MSG_V01_MAX_MSG_LEN 6156
-extern struct qmi_elem_info wlfw_athdiag_read_resp_msg_v01_ei[];
+extern const struct qmi_elem_info wlfw_athdiag_read_resp_msg_v01_ei[];
struct wlfw_athdiag_write_req_msg_v01 {
u32 offset;
@@ -504,28 +504,28 @@ struct wlfw_athdiag_write_req_msg_v01 {
};
#define WLFW_ATHDIAG_WRITE_REQ_MSG_V01_MAX_MSG_LEN 6163
-extern struct qmi_elem_info wlfw_athdiag_write_req_msg_v01_ei[];
+extern const struct qmi_elem_info wlfw_athdiag_write_req_msg_v01_ei[];
struct wlfw_athdiag_write_resp_msg_v01 {
struct qmi_response_type_v01 resp;
};
#define WLFW_ATHDIAG_WRITE_RESP_MSG_V01_MAX_MSG_LEN 7
-extern struct qmi_elem_info wlfw_athdiag_write_resp_msg_v01_ei[];
+extern const struct qmi_elem_info wlfw_athdiag_write_resp_msg_v01_ei[];
struct wlfw_vbatt_req_msg_v01 {
u64 voltage_uv;
};
#define WLFW_VBATT_REQ_MSG_V01_MAX_MSG_LEN 11
-extern struct qmi_elem_info wlfw_vbatt_req_msg_v01_ei[];
+extern const struct qmi_elem_info wlfw_vbatt_req_msg_v01_ei[];
struct wlfw_vbatt_resp_msg_v01 {
struct qmi_response_type_v01 resp;
};
#define WLFW_VBATT_RESP_MSG_V01_MAX_MSG_LEN 7
-extern struct qmi_elem_info wlfw_vbatt_resp_msg_v01_ei[];
+extern const struct qmi_elem_info wlfw_vbatt_resp_msg_v01_ei[];
struct wlfw_mac_addr_req_msg_v01 {
u8 mac_addr_valid;
@@ -533,14 +533,14 @@ struct wlfw_mac_addr_req_msg_v01 {
};
#define WLFW_MAC_ADDR_REQ_MSG_V01_MAX_MSG_LEN 9
-extern struct qmi_elem_info wlfw_mac_addr_req_msg_v01_ei[];
+extern const struct qmi_elem_info wlfw_mac_addr_req_msg_v01_ei[];
struct wlfw_mac_addr_resp_msg_v01 {
struct qmi_response_type_v01 resp;
};
#define WLFW_MAC_ADDR_RESP_MSG_V01_MAX_MSG_LEN 7
-extern struct qmi_elem_info wlfw_mac_addr_resp_msg_v01_ei[];
+extern const struct qmi_elem_info wlfw_mac_addr_resp_msg_v01_ei[];
#define QMI_WLFW_MAX_NUM_GPIO_V01 32
struct wlfw_host_cap_req_msg_v01 {
@@ -574,15 +574,15 @@ struct wlfw_host_cap_req_msg_v01 {
};
#define WLFW_HOST_CAP_REQ_MSG_V01_MAX_MSG_LEN 189
-extern struct qmi_elem_info wlfw_host_cap_req_msg_v01_ei[];
-extern struct qmi_elem_info wlfw_host_cap_8bit_req_msg_v01_ei[];
+extern const struct qmi_elem_info wlfw_host_cap_req_msg_v01_ei[];
+extern const struct qmi_elem_info wlfw_host_cap_8bit_req_msg_v01_ei[];
struct wlfw_host_cap_resp_msg_v01 {
struct qmi_response_type_v01 resp;
};
#define WLFW_HOST_CAP_RESP_MSG_V01_MAX_MSG_LEN 7
-extern struct qmi_elem_info wlfw_host_cap_resp_msg_v01_ei[];
+extern const struct qmi_elem_info wlfw_host_cap_resp_msg_v01_ei[];
struct wlfw_request_mem_ind_msg_v01 {
u32 mem_seg_len;
@@ -590,7 +590,7 @@ struct wlfw_request_mem_ind_msg_v01 {
};
#define WLFW_REQUEST_MEM_IND_MSG_V01_MAX_MSG_LEN 564
-extern struct qmi_elem_info wlfw_request_mem_ind_msg_v01_ei[];
+extern const struct qmi_elem_info wlfw_request_mem_ind_msg_v01_ei[];
struct wlfw_respond_mem_req_msg_v01 {
u32 mem_seg_len;
@@ -598,28 +598,28 @@ struct wlfw_respond_mem_req_msg_v01 {
};
#define WLFW_RESPOND_MEM_REQ_MSG_V01_MAX_MSG_LEN 260
-extern struct qmi_elem_info wlfw_respond_mem_req_msg_v01_ei[];
+extern const struct qmi_elem_info wlfw_respond_mem_req_msg_v01_ei[];
struct wlfw_respond_mem_resp_msg_v01 {
struct qmi_response_type_v01 resp;
};
#define WLFW_RESPOND_MEM_RESP_MSG_V01_MAX_MSG_LEN 7
-extern struct qmi_elem_info wlfw_respond_mem_resp_msg_v01_ei[];
+extern const struct qmi_elem_info wlfw_respond_mem_resp_msg_v01_ei[];
struct wlfw_mem_ready_ind_msg_v01 {
char placeholder;
};
#define WLFW_MEM_READY_IND_MSG_V01_MAX_MSG_LEN 0
-extern struct qmi_elem_info wlfw_mem_ready_ind_msg_v01_ei[];
+extern const struct qmi_elem_info wlfw_mem_ready_ind_msg_v01_ei[];
struct wlfw_fw_init_done_ind_msg_v01 {
char placeholder;
};
#define WLFW_FW_INIT_DONE_IND_MSG_V01_MAX_MSG_LEN 0
-extern struct qmi_elem_info wlfw_fw_init_done_ind_msg_v01_ei[];
+extern const struct qmi_elem_info wlfw_fw_init_done_ind_msg_v01_ei[];
struct wlfw_rejuvenate_ind_msg_v01 {
u8 cause_for_rejuvenation_valid;
@@ -633,21 +633,21 @@ struct wlfw_rejuvenate_ind_msg_v01 {
};
#define WLFW_REJUVENATE_IND_MSG_V01_MAX_MSG_LEN 144
-extern struct qmi_elem_info wlfw_rejuvenate_ind_msg_v01_ei[];
+extern const struct qmi_elem_info wlfw_rejuvenate_ind_msg_v01_ei[];
struct wlfw_rejuvenate_ack_req_msg_v01 {
char placeholder;
};
#define WLFW_REJUVENATE_ACK_REQ_MSG_V01_MAX_MSG_LEN 0
-extern struct qmi_elem_info wlfw_rejuvenate_ack_req_msg_v01_ei[];
+extern const struct qmi_elem_info wlfw_rejuvenate_ack_req_msg_v01_ei[];
struct wlfw_rejuvenate_ack_resp_msg_v01 {
struct qmi_response_type_v01 resp;
};
#define WLFW_REJUVENATE_ACK_RESP_MSG_V01_MAX_MSG_LEN 7
-extern struct qmi_elem_info wlfw_rejuvenate_ack_resp_msg_v01_ei[];
+extern const struct qmi_elem_info wlfw_rejuvenate_ack_resp_msg_v01_ei[];
struct wlfw_dynamic_feature_mask_req_msg_v01 {
u8 mask_valid;
@@ -655,7 +655,7 @@ struct wlfw_dynamic_feature_mask_req_msg_v01 {
};
#define WLFW_DYNAMIC_FEATURE_MASK_REQ_MSG_V01_MAX_MSG_LEN 11
-extern struct qmi_elem_info wlfw_dynamic_feature_mask_req_msg_v01_ei[];
+extern const struct qmi_elem_info wlfw_dynamic_feature_mask_req_msg_v01_ei[];
struct wlfw_dynamic_feature_mask_resp_msg_v01 {
struct qmi_response_type_v01 resp;
@@ -666,7 +666,7 @@ struct wlfw_dynamic_feature_mask_resp_msg_v01 {
};
#define WLFW_DYNAMIC_FEATURE_MASK_RESP_MSG_V01_MAX_MSG_LEN 29
-extern struct qmi_elem_info wlfw_dynamic_feature_mask_resp_msg_v01_ei[];
+extern const struct qmi_elem_info wlfw_dynamic_feature_mask_resp_msg_v01_ei[];
struct wlfw_m3_info_req_msg_v01 {
u64 addr;
@@ -674,20 +674,20 @@ struct wlfw_m3_info_req_msg_v01 {
};
#define WLFW_M3_INFO_REQ_MSG_V01_MAX_MSG_LEN 18
-extern struct qmi_elem_info wlfw_m3_info_req_msg_v01_ei[];
+extern const struct qmi_elem_info wlfw_m3_info_req_msg_v01_ei[];
struct wlfw_m3_info_resp_msg_v01 {
struct qmi_response_type_v01 resp;
};
#define WLFW_M3_INFO_RESP_MSG_V01_MAX_MSG_LEN 7
-extern struct qmi_elem_info wlfw_m3_info_resp_msg_v01_ei[];
+extern const struct qmi_elem_info wlfw_m3_info_resp_msg_v01_ei[];
struct wlfw_xo_cal_ind_msg_v01 {
u8 xo_cal_data;
};
#define WLFW_XO_CAL_IND_MSG_V01_MAX_MSG_LEN 4
-extern struct qmi_elem_info wlfw_xo_cal_ind_msg_v01_ei[];
+extern const struct qmi_elem_info wlfw_xo_cal_ind_msg_v01_ei[];
#endif
diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c
index 0ab2f190f1ec..edf78df9b12f 100644
--- a/drivers/net/wireless/ath/ath11k/core.c
+++ b/drivers/net/wireless/ath/ath11k/core.c
@@ -195,6 +195,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
.tcl_ring_retry = true,
.tx_ring_size = DP_TCL_DATA_RING_SIZE,
.smp2p_wow_exit = false,
+ .support_fw_mac_sequence = false,
},
{
.name = "qca6390 hw2.0",
@@ -277,6 +278,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
.tcl_ring_retry = true,
.tx_ring_size = DP_TCL_DATA_RING_SIZE,
.smp2p_wow_exit = false,
+ .support_fw_mac_sequence = true,
},
{
.name = "qcn9074 hw1.0",
@@ -356,6 +358,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
.tcl_ring_retry = true,
.tx_ring_size = DP_TCL_DATA_RING_SIZE,
.smp2p_wow_exit = false,
+ .support_fw_mac_sequence = false,
},
{
.name = "wcn6855 hw2.0",
@@ -438,6 +441,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
.tcl_ring_retry = true,
.tx_ring_size = DP_TCL_DATA_RING_SIZE,
.smp2p_wow_exit = false,
+ .support_fw_mac_sequence = true,
},
{
.name = "wcn6855 hw2.1",
@@ -519,6 +523,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
.tcl_ring_retry = true,
.tx_ring_size = DP_TCL_DATA_RING_SIZE,
.smp2p_wow_exit = false,
+ .support_fw_mac_sequence = true,
},
{
.name = "wcn6750 hw1.0",
@@ -597,6 +602,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
.tcl_ring_retry = false,
.tx_ring_size = DP_TCL_DATA_RING_SIZE_WCN6750,
.smp2p_wow_exit = true,
+ .support_fw_mac_sequence = true,
},
};
diff --git a/drivers/net/wireless/ath/ath11k/hw.h b/drivers/net/wireless/ath/ath11k/hw.h
index 8a3f24862edc..0c5ef8a526d8 100644
--- a/drivers/net/wireless/ath/ath11k/hw.h
+++ b/drivers/net/wireless/ath/ath11k/hw.h
@@ -219,6 +219,7 @@ struct ath11k_hw_params {
bool tcl_ring_retry;
u32 tx_ring_size;
bool smp2p_wow_exit;
+ bool support_fw_mac_sequence;
};
struct ath11k_hw_ops {
diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c
index 6426a38f8961..9e923ecb0891 100644
--- a/drivers/net/wireless/ath/ath11k/mac.c
+++ b/drivers/net/wireless/ath/ath11k/mac.c
@@ -8010,6 +8010,7 @@ ath11k_mac_op_reconfig_complete(struct ieee80211_hw *hw,
struct ath11k *ar = hw->priv;
struct ath11k_base *ab = ar->ab;
int recovery_count;
+ struct ath11k_vif *arvif;
if (reconfig_type != IEEE80211_RECONFIG_TYPE_RESTART)
return;
@@ -8045,6 +8046,12 @@ ath11k_mac_op_reconfig_complete(struct ieee80211_hw *hw,
ath11k_dbg(ab, ATH11K_DBG_BOOT, "reset success\n");
}
}
+ if (ar->ab->hw_params.support_fw_mac_sequence) {
+ list_for_each_entry(arvif, &ar->arvifs, list) {
+ if (arvif->is_up && arvif->vdev_type == WMI_VDEV_TYPE_STA)
+ ieee80211_hw_restart_disconnect(arvif->vif);
+ }
+ }
}
mutex_unlock(&ar->conf_mutex);
diff --git a/drivers/net/wireless/ath/ath11k/mac.h b/drivers/net/wireless/ath/ath11k/mac.h
index 2a0d3afb0c99..0231783ad754 100644
--- a/drivers/net/wireless/ath/ath11k/mac.h
+++ b/drivers/net/wireless/ath/ath11k/mac.h
@@ -163,7 +163,7 @@ void ath11k_mac_drain_tx(struct ath11k *ar);
void ath11k_mac_peer_cleanup_all(struct ath11k *ar);
int ath11k_mac_tx_mgmt_pending_free(int buf_id, void *skb, void *ctx);
u8 ath11k_mac_bw_to_mac80211_bw(u8 bw);
-u32 ath11k_mac_he_gi_to_nl80211_he_gi(u8 sgi);
+enum nl80211_he_gi ath11k_mac_he_gi_to_nl80211_he_gi(u8 sgi);
enum nl80211_he_ru_alloc ath11k_mac_phy_he_ru_to_nl80211_he_ru_alloc(u16 ru_phy);
enum nl80211_he_ru_alloc ath11k_mac_he_ru_tones_to_nl80211_he_ru_alloc(u16 ru_tones);
enum ath11k_supported_bw ath11k_mac_mac80211_bw_to_ath11k_bw(enum rate_info_bw bw);
diff --git a/drivers/net/wireless/ath/ath11k/qmi.c b/drivers/net/wireless/ath/ath11k/qmi.c
index 145f20a681bd..ab923e24b0a9 100644
--- a/drivers/net/wireless/ath/ath11k/qmi.c
+++ b/drivers/net/wireless/ath/ath11k/qmi.c
@@ -29,7 +29,7 @@ module_param_named(cold_boot_cal, ath11k_cold_boot_cal, bool, 0644);
MODULE_PARM_DESC(cold_boot_cal,
"Decrease the channel switch time but increase the driver load time (Default: true)");
-static struct qmi_elem_info qmi_wlanfw_host_cap_req_msg_v01_ei[] = {
+static const struct qmi_elem_info qmi_wlanfw_host_cap_req_msg_v01_ei[] = {
{
.data_type = QMI_OPT_FLAG,
.elem_len = 1,
@@ -280,7 +280,7 @@ static struct qmi_elem_info qmi_wlanfw_host_cap_req_msg_v01_ei[] = {
},
};
-static struct qmi_elem_info qmi_wlanfw_host_cap_resp_msg_v01_ei[] = {
+static const struct qmi_elem_info qmi_wlanfw_host_cap_resp_msg_v01_ei[] = {
{
.data_type = QMI_STRUCT,
.elem_len = 1,
@@ -297,7 +297,7 @@ static struct qmi_elem_info qmi_wlanfw_host_cap_resp_msg_v01_ei[] = {
},
};
-static struct qmi_elem_info qmi_wlanfw_ind_register_req_msg_v01_ei[] = {
+static const struct qmi_elem_info qmi_wlanfw_ind_register_req_msg_v01_ei[] = {
{
.data_type = QMI_OPT_FLAG,
.elem_len = 1,
@@ -522,7 +522,7 @@ static struct qmi_elem_info qmi_wlanfw_ind_register_req_msg_v01_ei[] = {
},
};
-static struct qmi_elem_info qmi_wlanfw_ind_register_resp_msg_v01_ei[] = {
+static const struct qmi_elem_info qmi_wlanfw_ind_register_resp_msg_v01_ei[] = {
{
.data_type = QMI_STRUCT,
.elem_len = 1,
@@ -558,7 +558,7 @@ static struct qmi_elem_info qmi_wlanfw_ind_register_resp_msg_v01_ei[] = {
},
};
-static struct qmi_elem_info qmi_wlanfw_mem_cfg_s_v01_ei[] = {
+static const struct qmi_elem_info qmi_wlanfw_mem_cfg_s_v01_ei[] = {
{
.data_type = QMI_UNSIGNED_8_BYTE,
.elem_len = 1,
@@ -590,7 +590,7 @@ static struct qmi_elem_info qmi_wlanfw_mem_cfg_s_v01_ei[] = {
},
};
-static struct qmi_elem_info qmi_wlanfw_mem_seg_s_v01_ei[] = {
+static const struct qmi_elem_info qmi_wlanfw_mem_seg_s_v01_ei[] = {
{
.data_type = QMI_UNSIGNED_4_BYTE,
.elem_len = 1,
@@ -632,7 +632,7 @@ static struct qmi_elem_info qmi_wlanfw_mem_seg_s_v01_ei[] = {
},
};
-static struct qmi_elem_info qmi_wlanfw_request_mem_ind_msg_v01_ei[] = {
+static const struct qmi_elem_info qmi_wlanfw_request_mem_ind_msg_v01_ei[] = {
{
.data_type = QMI_DATA_LEN,
.elem_len = 1,
@@ -659,7 +659,7 @@ static struct qmi_elem_info qmi_wlanfw_request_mem_ind_msg_v01_ei[] = {
},
};
-static struct qmi_elem_info qmi_wlanfw_mem_seg_resp_s_v01_ei[] = {
+static const struct qmi_elem_info qmi_wlanfw_mem_seg_resp_s_v01_ei[] = {
{
.data_type = QMI_UNSIGNED_8_BYTE,
.elem_len = 1,
@@ -699,7 +699,7 @@ static struct qmi_elem_info qmi_wlanfw_mem_seg_resp_s_v01_ei[] = {
},
};
-static struct qmi_elem_info qmi_wlanfw_respond_mem_req_msg_v01_ei[] = {
+static const struct qmi_elem_info qmi_wlanfw_respond_mem_req_msg_v01_ei[] = {
{
.data_type = QMI_DATA_LEN,
.elem_len = 1,
@@ -726,7 +726,7 @@ static struct qmi_elem_info qmi_wlanfw_respond_mem_req_msg_v01_ei[] = {
},
};
-static struct qmi_elem_info qmi_wlanfw_respond_mem_resp_msg_v01_ei[] = {
+static const struct qmi_elem_info qmi_wlanfw_respond_mem_resp_msg_v01_ei[] = {
{
.data_type = QMI_STRUCT,
.elem_len = 1,
@@ -744,7 +744,7 @@ static struct qmi_elem_info qmi_wlanfw_respond_mem_resp_msg_v01_ei[] = {
},
};
-static struct qmi_elem_info qmi_wlanfw_cap_req_msg_v01_ei[] = {
+static const struct qmi_elem_info qmi_wlanfw_cap_req_msg_v01_ei[] = {
{
.data_type = QMI_EOTI,
.array_type = NO_ARRAY,
@@ -752,7 +752,7 @@ static struct qmi_elem_info qmi_wlanfw_cap_req_msg_v01_ei[] = {
},
};
-static struct qmi_elem_info qmi_wlanfw_device_info_req_msg_v01_ei[] = {
+static const struct qmi_elem_info qmi_wlanfw_device_info_req_msg_v01_ei[] = {
{
.data_type = QMI_EOTI,
.array_type = NO_ARRAY,
@@ -760,7 +760,7 @@ static struct qmi_elem_info qmi_wlanfw_device_info_req_msg_v01_ei[] = {
},
};
-static struct qmi_elem_info qmi_wlfw_device_info_resp_msg_v01_ei[] = {
+static const struct qmi_elem_info qmi_wlfw_device_info_resp_msg_v01_ei[] = {
{
.data_type = QMI_STRUCT,
.elem_len = 1,
@@ -814,7 +814,7 @@ static struct qmi_elem_info qmi_wlfw_device_info_resp_msg_v01_ei[] = {
},
};
-static struct qmi_elem_info qmi_wlanfw_rf_chip_info_s_v01_ei[] = {
+static const struct qmi_elem_info qmi_wlanfw_rf_chip_info_s_v01_ei[] = {
{
.data_type = QMI_UNSIGNED_4_BYTE,
.elem_len = 1,
@@ -840,7 +840,7 @@ static struct qmi_elem_info qmi_wlanfw_rf_chip_info_s_v01_ei[] = {
},
};
-static struct qmi_elem_info qmi_wlanfw_rf_board_info_s_v01_ei[] = {
+static const struct qmi_elem_info qmi_wlanfw_rf_board_info_s_v01_ei[] = {
{
.data_type = QMI_UNSIGNED_4_BYTE,
.elem_len = 1,
@@ -857,7 +857,7 @@ static struct qmi_elem_info qmi_wlanfw_rf_board_info_s_v01_ei[] = {
},
};
-static struct qmi_elem_info qmi_wlanfw_soc_info_s_v01_ei[] = {
+static const struct qmi_elem_info qmi_wlanfw_soc_info_s_v01_ei[] = {
{
.data_type = QMI_UNSIGNED_4_BYTE,
.elem_len = 1,
@@ -873,7 +873,7 @@ static struct qmi_elem_info qmi_wlanfw_soc_info_s_v01_ei[] = {
},
};
-static struct qmi_elem_info qmi_wlanfw_fw_version_info_s_v01_ei[] = {
+static const struct qmi_elem_info qmi_wlanfw_fw_version_info_s_v01_ei[] = {
{
.data_type = QMI_UNSIGNED_4_BYTE,
.elem_len = 1,
@@ -899,7 +899,7 @@ static struct qmi_elem_info qmi_wlanfw_fw_version_info_s_v01_ei[] = {
},
};
-static struct qmi_elem_info qmi_wlanfw_cap_resp_msg_v01_ei[] = {
+static const struct qmi_elem_info qmi_wlanfw_cap_resp_msg_v01_ei[] = {
{
.data_type = QMI_STRUCT,
.elem_len = 1,
@@ -1100,7 +1100,7 @@ static struct qmi_elem_info qmi_wlanfw_cap_resp_msg_v01_ei[] = {
},
};
-static struct qmi_elem_info qmi_wlanfw_bdf_download_req_msg_v01_ei[] = {
+static const struct qmi_elem_info qmi_wlanfw_bdf_download_req_msg_v01_ei[] = {
{
.data_type = QMI_UNSIGNED_1_BYTE,
.elem_len = 1,
@@ -1235,7 +1235,7 @@ static struct qmi_elem_info qmi_wlanfw_bdf_download_req_msg_v01_ei[] = {
},
};
-static struct qmi_elem_info qmi_wlanfw_bdf_download_resp_msg_v01_ei[] = {
+static const struct qmi_elem_info qmi_wlanfw_bdf_download_resp_msg_v01_ei[] = {
{
.data_type = QMI_STRUCT,
.elem_len = 1,
@@ -1253,7 +1253,7 @@ static struct qmi_elem_info qmi_wlanfw_bdf_download_resp_msg_v01_ei[] = {
},
};
-static struct qmi_elem_info qmi_wlanfw_m3_info_req_msg_v01_ei[] = {
+static const struct qmi_elem_info qmi_wlanfw_m3_info_req_msg_v01_ei[] = {
{
.data_type = QMI_UNSIGNED_8_BYTE,
.elem_len = 1,
@@ -1277,7 +1277,7 @@ static struct qmi_elem_info qmi_wlanfw_m3_info_req_msg_v01_ei[] = {
},
};
-static struct qmi_elem_info qmi_wlanfw_m3_info_resp_msg_v01_ei[] = {
+static const struct qmi_elem_info qmi_wlanfw_m3_info_resp_msg_v01_ei[] = {
{
.data_type = QMI_STRUCT,
.elem_len = 1,
@@ -1294,7 +1294,7 @@ static struct qmi_elem_info qmi_wlanfw_m3_info_resp_msg_v01_ei[] = {
},
};
-static struct qmi_elem_info qmi_wlanfw_ce_tgt_pipe_cfg_s_v01_ei[] = {
+static const struct qmi_elem_info qmi_wlanfw_ce_tgt_pipe_cfg_s_v01_ei[] = {
{
.data_type = QMI_UNSIGNED_4_BYTE,
.elem_len = 1,
@@ -1347,7 +1347,7 @@ static struct qmi_elem_info qmi_wlanfw_ce_tgt_pipe_cfg_s_v01_ei[] = {
},
};
-static struct qmi_elem_info qmi_wlanfw_ce_svc_pipe_cfg_s_v01_ei[] = {
+static const struct qmi_elem_info qmi_wlanfw_ce_svc_pipe_cfg_s_v01_ei[] = {
{
.data_type = QMI_UNSIGNED_4_BYTE,
.elem_len = 1,
@@ -1382,7 +1382,7 @@ static struct qmi_elem_info qmi_wlanfw_ce_svc_pipe_cfg_s_v01_ei[] = {
},
};
-static struct qmi_elem_info qmi_wlanfw_shadow_reg_cfg_s_v01_ei[] = {
+static const struct qmi_elem_info qmi_wlanfw_shadow_reg_cfg_s_v01_ei[] = {
{
.data_type = QMI_UNSIGNED_2_BYTE,
.elem_len = 1,
@@ -1406,7 +1406,7 @@ static struct qmi_elem_info qmi_wlanfw_shadow_reg_cfg_s_v01_ei[] = {
},
};
-static struct qmi_elem_info qmi_wlanfw_shadow_reg_v2_cfg_s_v01_ei[] = {
+static const struct qmi_elem_info qmi_wlanfw_shadow_reg_v2_cfg_s_v01_ei[] = {
{
.data_type = QMI_UNSIGNED_4_BYTE,
.elem_len = 1,
@@ -1423,7 +1423,7 @@ static struct qmi_elem_info qmi_wlanfw_shadow_reg_v2_cfg_s_v01_ei[] = {
},
};
-static struct qmi_elem_info qmi_wlanfw_wlan_mode_req_msg_v01_ei[] = {
+static const struct qmi_elem_info qmi_wlanfw_wlan_mode_req_msg_v01_ei[] = {
{
.data_type = QMI_UNSIGNED_4_BYTE,
.elem_len = 1,
@@ -1458,7 +1458,7 @@ static struct qmi_elem_info qmi_wlanfw_wlan_mode_req_msg_v01_ei[] = {
},
};
-static struct qmi_elem_info qmi_wlanfw_wlan_mode_resp_msg_v01_ei[] = {
+static const struct qmi_elem_info qmi_wlanfw_wlan_mode_resp_msg_v01_ei[] = {
{
.data_type = QMI_STRUCT,
.elem_len = 1,
@@ -1476,7 +1476,7 @@ static struct qmi_elem_info qmi_wlanfw_wlan_mode_resp_msg_v01_ei[] = {
},
};
-static struct qmi_elem_info qmi_wlanfw_wlan_cfg_req_msg_v01_ei[] = {
+static const struct qmi_elem_info qmi_wlanfw_wlan_cfg_req_msg_v01_ei[] = {
{
.data_type = QMI_OPT_FLAG,
.elem_len = 1,
@@ -1615,7 +1615,7 @@ static struct qmi_elem_info qmi_wlanfw_wlan_cfg_req_msg_v01_ei[] = {
},
};
-static struct qmi_elem_info qmi_wlanfw_wlan_cfg_resp_msg_v01_ei[] = {
+static const struct qmi_elem_info qmi_wlanfw_wlan_cfg_resp_msg_v01_ei[] = {
{
.data_type = QMI_STRUCT,
.elem_len = 1,
@@ -1632,28 +1632,28 @@ static struct qmi_elem_info qmi_wlanfw_wlan_cfg_resp_msg_v01_ei[] = {
},
};
-static struct qmi_elem_info qmi_wlanfw_mem_ready_ind_msg_v01_ei[] = {
+static const struct qmi_elem_info qmi_wlanfw_mem_ready_ind_msg_v01_ei[] = {
{
.data_type = QMI_EOTI,
.array_type = NO_ARRAY,
},
};
-static struct qmi_elem_info qmi_wlanfw_fw_ready_ind_msg_v01_ei[] = {
+static const struct qmi_elem_info qmi_wlanfw_fw_ready_ind_msg_v01_ei[] = {
{
.data_type = QMI_EOTI,
.array_type = NO_ARRAY,
},
};
-static struct qmi_elem_info qmi_wlanfw_cold_boot_cal_done_ind_msg_v01_ei[] = {
+static const struct qmi_elem_info qmi_wlanfw_cold_boot_cal_done_ind_msg_v01_ei[] = {
{
.data_type = QMI_EOTI,
.array_type = NO_ARRAY,
},
};
-static struct qmi_elem_info qmi_wlanfw_wlan_ini_req_msg_v01_ei[] = {
+static const struct qmi_elem_info qmi_wlanfw_wlan_ini_req_msg_v01_ei[] = {
{
.data_type = QMI_OPT_FLAG,
.elem_len = 1,
@@ -1679,7 +1679,7 @@ static struct qmi_elem_info qmi_wlanfw_wlan_ini_req_msg_v01_ei[] = {
},
};
-static struct qmi_elem_info qmi_wlanfw_wlan_ini_resp_msg_v01_ei[] = {
+static const struct qmi_elem_info qmi_wlanfw_wlan_ini_resp_msg_v01_ei[] = {
{
.data_type = QMI_STRUCT,
.elem_len = 1,
@@ -1697,7 +1697,7 @@ static struct qmi_elem_info qmi_wlanfw_wlan_ini_resp_msg_v01_ei[] = {
},
};
-static struct qmi_elem_info qmi_wlfw_fw_init_done_ind_msg_v01_ei[] = {
+static const struct qmi_elem_info qmi_wlfw_fw_init_done_ind_msg_v01_ei[] = {
{
.data_type = QMI_EOTI,
.array_type = NO_ARRAY,
@@ -3090,6 +3090,9 @@ static const struct qmi_msg_handler ath11k_qmi_msg_handlers[] = {
sizeof(struct qmi_wlfw_fw_init_done_ind_msg_v01),
.fn = ath11k_qmi_msg_fw_init_done_cb,
},
+
+ /* end of list */
+ {},
};
static int ath11k_qmi_ops_new_server(struct qmi_handle *qmi_hdl,
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.c b/drivers/net/wireless/ath/ath9k/ar9003_mci.c
index 9899661f9a60..8d7efd80d97a 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_mci.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.c
@@ -585,7 +585,7 @@ static u32 ar9003_mci_wait_for_gpm(struct ath_hw *ah, u8 gpm_type,
{
struct ath_common *common = ath9k_hw_common(ah);
struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci;
- u32 *p_gpm = NULL, mismatch = 0, more_data;
+ u32 *p_gpm = NULL, more_data;
u32 offset;
u8 recv_type = 0, recv_opcode = 0;
bool b_is_bt_cal_done = (gpm_type == MCI_GPM_BT_CAL_DONE);
@@ -656,7 +656,6 @@ static u32 ar9003_mci_wait_for_gpm(struct ath_hw *ah, u8 gpm_type,
} else {
ath_dbg(common, MCI, "MCI GPM subtype not match 0x%x\n",
*(p_gpm + 1));
- mismatch++;
ar9003_mci_process_gpm_extra(ah, recv_type,
recv_opcode, p_gpm);
}
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index ba271a10d4ab..39abb59d8771 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -1678,7 +1678,6 @@ void ath9k_release_buffered_frames(struct ieee80211_hw *hw,
struct ieee80211_tx_info *info;
struct list_head bf_q;
struct ath_buf *bf_tail = NULL, *bf = NULL;
- int sent = 0;
int i, ret;
INIT_LIST_HEAD(&bf_q);
@@ -1707,7 +1706,6 @@ void ath9k_release_buffered_frames(struct ieee80211_hw *hw,
bf_tail = bf;
nframes--;
- sent++;
TX_STAT_INC(sc, txq->axq_qnum, a_queued_hw);
if (an->sta && skb_queue_empty(&tid->retry_q))
diff --git a/drivers/net/wireless/ath/carl9170/fwcmd.h b/drivers/net/wireless/ath/carl9170/fwcmd.h
index 4a500095555c..ff4b3b50250c 100644
--- a/drivers/net/wireless/ath/carl9170/fwcmd.h
+++ b/drivers/net/wireless/ath/carl9170/fwcmd.h
@@ -118,10 +118,10 @@ struct carl9170_reg_list {
} __packed;
struct carl9170_write_reg {
- struct {
+ DECLARE_FLEX_ARRAY(struct {
__le32 addr;
__le32 val;
- } regs[0] __packed;
+ } __packed, regs);
} __packed;
struct carl9170_write_reg_byte {
diff --git a/drivers/net/wireless/broadcom/b43/main.c b/drivers/net/wireless/broadcom/b43/main.c
index 298febbdffc0..92ca0b2ca286 100644
--- a/drivers/net/wireless/broadcom/b43/main.c
+++ b/drivers/net/wireless/broadcom/b43/main.c
@@ -5784,15 +5784,12 @@ void b43_controller_restart(struct b43_wldev *dev, const char *reason)
static void b43_print_driverinfo(void)
{
- const char *feat_pci = "", *feat_pcmcia = "", *feat_nphy = "",
+ const char *feat_pci = "", *feat_nphy = "",
*feat_leds = "", *feat_sdio = "";
#ifdef CONFIG_B43_PCI_AUTOSELECT
feat_pci = "P";
#endif
-#ifdef CONFIG_B43_PCMCIA
- feat_pcmcia = "M";
-#endif
#ifdef CONFIG_B43_PHY_N
feat_nphy = "N";
#endif
@@ -5803,9 +5800,8 @@ static void b43_print_driverinfo(void)
feat_sdio = "S";
#endif
printk(KERN_INFO "Broadcom 43xx driver loaded "
- "[ Features: %s%s%s%s%s ]\n",
- feat_pci, feat_pcmcia, feat_nphy,
- feat_leds, feat_sdio);
+ "[ Features: %s%s%s%s ]\n",
+ feat_pci, feat_nphy, feat_leds, feat_sdio);
}
static int __init b43_init(void)
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
index ae9507dec74a..bff3128c2f26 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
@@ -101,6 +101,9 @@
#define BRCMF_ASSOC_PARAMS_FIXED_SIZE \
(sizeof(struct brcmf_assoc_params_le) - sizeof(u16))
+#define BRCMF_MAX_CHANSPEC_LIST \
+ (BRCMF_DCMD_MEDLEN / sizeof(__le32) - 1)
+
struct brcmf_dump_survey {
u32 obss;
u32 ibss;
@@ -6840,6 +6843,13 @@ static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg,
band->channels[i].flags = IEEE80211_CHAN_DISABLED;
total = le32_to_cpu(list->count);
+ if (total > BRCMF_MAX_CHANSPEC_LIST) {
+ bphy_err(drvr, "Invalid count of channel Spec. (%u)\n",
+ total);
+ err = -EINVAL;
+ goto fail_pbuf;
+ }
+
for (i = 0; i < total; i++) {
ch.chspec = (u16)le32_to_cpu(list->element[i]);
cfg->d11inf.decchspec(&ch);
@@ -6985,6 +6995,13 @@ static int brcmf_enable_bw40_2g(struct brcmf_cfg80211_info *cfg)
band = cfg_to_wiphy(cfg)->bands[NL80211_BAND_2GHZ];
list = (struct brcmf_chanspec_list *)pbuf;
num_chan = le32_to_cpu(list->count);
+ if (num_chan > BRCMF_MAX_CHANSPEC_LIST) {
+ bphy_err(drvr, "Invalid count of channel Spec. (%u)\n",
+ num_chan);
+ kfree(pbuf);
+ return -EINVAL;
+ }
+
for (i = 0; i < num_chan; i++) {
ch.chspec = (u16)le32_to_cpu(list->element[i]);
cfg->d11inf.decchspec(&ch);
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
index 22344e68fd59..4a309e5a5707 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
@@ -110,9 +110,9 @@ static int brcmf_c_download(struct brcmf_if *ifp, u16 flag,
dload_buf->dload_type = cpu_to_le16(DL_TYPE_CLM);
dload_buf->len = cpu_to_le32(len);
dload_buf->crc = cpu_to_le32(0);
- len = sizeof(*dload_buf) + len - 1;
- err = brcmf_fil_iovar_data_set(ifp, "clmload", dload_buf, len);
+ err = brcmf_fil_iovar_data_set(ifp, "clmload", dload_buf,
+ struct_size(dload_buf, data, len));
return err;
}
@@ -139,7 +139,8 @@ static int brcmf_c_process_clm_blob(struct brcmf_if *ifp)
return 0;
}
- chunk_buf = kzalloc(sizeof(*chunk_buf) + MAX_CHUNK_LEN - 1, GFP_KERNEL);
+ chunk_buf = kzalloc(struct_size(chunk_buf, data, MAX_CHUNK_LEN),
+ GFP_KERNEL);
if (!chunk_buf) {
err = -ENOMEM;
goto done;
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
index f518e025d6e4..04e1beedfd81 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
@@ -943,7 +943,7 @@ struct brcmf_dload_data_le {
__le16 dload_type;
__le32 len;
__le32 crc;
- u8 data[1];
+ u8 data[];
};
/**
@@ -1049,7 +1049,7 @@ struct brcmf_gscan_config {
u8 count_of_channel_buckets;
u8 retry_threshold;
__le16 lost_ap_window;
- struct brcmf_gscan_bucket_config bucket[1];
+ struct brcmf_gscan_bucket_config bucket[];
};
/**
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c
index 170c61c8136c..05f66ab13bed 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c
@@ -405,7 +405,7 @@ static int brcmf_pno_config_sched_scans(struct brcmf_if *ifp)
if (n_buckets < 0)
return n_buckets;
- gsz = sizeof(*gscan_cfg) + (n_buckets - 1) * sizeof(*buckets);
+ gsz = struct_size(gscan_cfg, bucket, n_buckets);
gscan_cfg = kzalloc(gsz, GFP_KERNEL);
if (!gscan_cfg) {
err = -ENOMEM;
@@ -434,8 +434,8 @@ static int brcmf_pno_config_sched_scans(struct brcmf_if *ifp)
gscan_cfg->flags = BRCMF_GSCAN_CFG_ALL_BUCKETS_IN_1ST_SCAN;
gscan_cfg->count_of_channel_buckets = n_buckets;
- memcpy(&gscan_cfg->bucket[0], buckets,
- n_buckets * sizeof(*buckets));
+ memcpy(gscan_cfg->bucket, buckets,
+ array_size(n_buckets, sizeof(*buckets)));
err = brcmf_fil_iovar_data_set(ifp, "pfn_gscan_cfg", gscan_cfg, gsz);
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h b/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h
index 9b7caf968346..e3eda251c728 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-2021 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2022 Intel Corporation
* Copyright (C) 2017 Intel Deutschland GmbH
*/
#ifndef __iwl_fw_api_mac_h__
@@ -398,7 +398,7 @@ struct iwl_he_backoff_conf {
* @IWL_HE_PKT_EXT_64QAM: 64-QAM
* @IWL_HE_PKT_EXT_256QAM: 256-QAM
* @IWL_HE_PKT_EXT_1024QAM: 1024-QAM
- * @IWL_HE_PKT_EXT_RESERVED: reserved value
+ * @IWL_HE_PKT_EXT_4096QAM: 4096-QAM, for EHT only
* @IWL_HE_PKT_EXT_NONE: not defined
*/
enum iwl_he_pkt_ext_constellations {
@@ -408,7 +408,7 @@ enum iwl_he_pkt_ext_constellations {
IWL_HE_PKT_EXT_64QAM,
IWL_HE_PKT_EXT_256QAM,
IWL_HE_PKT_EXT_1024QAM,
- IWL_HE_PKT_EXT_RESERVED,
+ IWL_HE_PKT_EXT_4096QAM,
IWL_HE_PKT_EXT_NONE,
};
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 e66f77924f83..2f7d8558becd 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/phy-ctxt.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/phy-ctxt.h
@@ -13,10 +13,12 @@
#define PHY_BAND_6 (2)
/* Supported channel width, vary if there is VHT support */
-#define PHY_VHT_CHANNEL_MODE20 (0x0)
-#define PHY_VHT_CHANNEL_MODE40 (0x1)
-#define PHY_VHT_CHANNEL_MODE80 (0x2)
-#define PHY_VHT_CHANNEL_MODE160 (0x3)
+#define IWL_PHY_CHANNEL_MODE20 0x0
+#define IWL_PHY_CHANNEL_MODE40 0x1
+#define IWL_PHY_CHANNEL_MODE80 0x2
+#define IWL_PHY_CHANNEL_MODE160 0x3
+/* and 320 MHz for EHT */
+#define IWL_PHY_CHANNEL_MODE320 0x4
/*
* Control channel position:
@@ -24,20 +26,17 @@
* For VHT - bit-2 marks if the control is lower/upper relative to center-freq
* bits-1:0 mark the distance from the center freq. for 20Mhz, offset is 0.
* center_freq
- * |
- * 40Mhz |_______|_______|
- * 80Mhz |_______|_______|_______|_______|
- * 160Mhz |_______|_______|_______|_______|_______|_______|_______|_______|
- * code 011 010 001 000 | 100 101 110 111
+ * For EHT - bit-3 is used for extended distance
+ * |
+ * 40Mhz |____|____|
+ * 80Mhz |____|____|____|____|
+ * 160Mhz |____|____|____|____|____|____|____|____|
+ * 320MHz |____|____|____|____|____|____|____|____|____|____|____|____|____|____|____|____|
+ * code 1011 1010 1001 1000 0011 0010 0001 0000 0100 0101 0110 0111 1100 1101 1110 1111
*/
-#define PHY_VHT_CTRL_POS_1_BELOW (0x0)
-#define PHY_VHT_CTRL_POS_2_BELOW (0x1)
-#define PHY_VHT_CTRL_POS_3_BELOW (0x2)
-#define PHY_VHT_CTRL_POS_4_BELOW (0x3)
-#define PHY_VHT_CTRL_POS_1_ABOVE (0x4)
-#define PHY_VHT_CTRL_POS_2_ABOVE (0x5)
-#define PHY_VHT_CTRL_POS_3_ABOVE (0x6)
-#define PHY_VHT_CTRL_POS_4_ABOVE (0x7)
+#define IWL_PHY_CTRL_POS_ABOVE 0x4
+#define IWL_PHY_CTRL_POS_OFFS_EXT 0x8
+#define IWL_PHY_CTRL_POS_OFFS_MSK 0x3
/*
* struct iwl_fw_channel_info_v1 - channel information
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h b/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h
index 687f804c46b7..ddacd5b45aea 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h
@@ -36,14 +36,14 @@ enum iwl_tlc_mng_cfg_flags {
* @IWL_TLC_MNG_CH_WIDTH_40MHZ: 40MHZ channel
* @IWL_TLC_MNG_CH_WIDTH_80MHZ: 80MHZ channel
* @IWL_TLC_MNG_CH_WIDTH_160MHZ: 160MHZ channel
- * @IWL_TLC_MNG_CH_WIDTH_LAST: maximum value
+ * @IWL_TLC_MNG_CH_WIDTH_320MHZ: 320MHZ channel
*/
enum iwl_tlc_mng_cfg_cw {
IWL_TLC_MNG_CH_WIDTH_20MHZ,
IWL_TLC_MNG_CH_WIDTH_40MHZ,
IWL_TLC_MNG_CH_WIDTH_80MHZ,
IWL_TLC_MNG_CH_WIDTH_160MHZ,
- IWL_TLC_MNG_CH_WIDTH_LAST = IWL_TLC_MNG_CH_WIDTH_160MHZ,
+ IWL_TLC_MNG_CH_WIDTH_320MHZ,
};
/**
@@ -64,8 +64,7 @@ enum iwl_tlc_mng_cfg_chains {
* @IWL_TLC_MNG_MODE_HT: enable HT
* @IWL_TLC_MNG_MODE_VHT: enable VHT
* @IWL_TLC_MNG_MODE_HE: enable HE
- * @IWL_TLC_MNG_MODE_INVALID: invalid value
- * @IWL_TLC_MNG_MODE_NUM: a count of possible modes
+ * @IWL_TLC_MNG_MODE_EHT: enable EHT
*/
enum iwl_tlc_mng_cfg_mode {
IWL_TLC_MNG_MODE_CCK = 0,
@@ -74,8 +73,7 @@ enum iwl_tlc_mng_cfg_mode {
IWL_TLC_MNG_MODE_HT,
IWL_TLC_MNG_MODE_VHT,
IWL_TLC_MNG_MODE_HE,
- IWL_TLC_MNG_MODE_INVALID,
- IWL_TLC_MNG_MODE_NUM = IWL_TLC_MNG_MODE_INVALID,
+ IWL_TLC_MNG_MODE_EHT,
};
/**
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/rs.c b/drivers/net/wireless/intel/iwlwifi/fw/rs.c
index a835214611ce..e128d2e07f38 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/rs.c
+++ b/drivers/net/wireless/intel/iwlwifi/fw/rs.c
@@ -218,6 +218,8 @@ int rs_pretty_print_rate(char *buf, int bufsz, const u32 rate)
type = "HT";
else if (format == RATE_MCS_HE_MSK)
type = "HE";
+ else if (format == RATE_MCS_EHT_MSK)
+ type = "EHT";
else
type = "Unknown"; /* shouldn't happen */
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c
index a2203f661321..ab7065c93826 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c
@@ -1971,3 +1971,6 @@ MODULE_PARM_DESC(remove_when_gone,
module_param_named(disable_11ax, iwlwifi_mod_params.disable_11ax, bool,
S_IRUGO);
MODULE_PARM_DESC(disable_11ax, "Disable HE capabilities (default: false)");
+
+module_param_named(disable_11be, iwlwifi_mod_params.disable_11be, bool, 0444);
+MODULE_PARM_DESC(disable_11be, "Disable EHT capabilities (default: false)");
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.h b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.h
index 0efffb6eeb1e..baa643386018 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright (C) 2005-2014, 2018 Intel Corporation
+ * Copyright (C) 2005-2014, 2018, 2020-2022 Intel Corporation
* Copyright (C) 2015 Intel Mobile Communications GmbH
*/
#ifndef __iwl_eeprom_parse_h__
@@ -31,6 +31,7 @@ struct iwl_nvm_data {
bool sku_cap_amt_enable;
bool sku_cap_ipan_enable;
bool sku_cap_mimo_disabled;
+ bool sku_cap_11be_enable;
u16 radio_cfg_type;
u8 radio_cfg_step;
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h b/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h
index d0b4d02bdab9..1cf26ab4f488 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h
@@ -62,6 +62,7 @@ enum iwl_uapsd_disable {
* @disable_11ac: disable VHT capabilities, default = false.
* @remove_when_gone: remove an inaccessible device from the PCIe bus.
* @enable_ini: enable new FW debug infratructure (INI TLVs)
+ * @disable_11be: disable EHT capabilities, default = false.
*/
struct iwl_mod_params {
int swcrypto;
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c
index 9040da3dcce3..476b90f32626 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-2021 Intel Corporation
+ * Copyright (C) 2005-2014, 2018-2022 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
*/
@@ -546,7 +546,7 @@ static const u8 iwl_vendor_caps[] = {
0x00
};
-static const struct ieee80211_sband_iftype_data iwl_he_capa[] = {
+static const struct ieee80211_sband_iftype_data iwl_he_eht_capa[] = {
{
.types_mask = BIT(NL80211_IFTYPE_STATION),
.he_cap = {
@@ -571,10 +571,6 @@ static const struct ieee80211_sband_iftype_data iwl_he_capa[] = {
IEEE80211_HE_MAC_CAP5_UL_2x996_TONE_RU |
IEEE80211_HE_MAC_CAP5_HE_DYNAMIC_SM_PS |
IEEE80211_HE_MAC_CAP5_HT_VHT_TRIG_FRAME_RX,
- .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,
.phy_cap_info[1] =
IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_MASK |
IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A |
@@ -631,6 +627,78 @@ static const struct ieee80211_sband_iftype_data iwl_he_capa[] = {
*/
.ppe_thres = {0x61, 0x1c, 0xc7, 0x71},
},
+ .eht_cap = {
+ .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,
+ .phy_cap_info[0] =
+ IEEE80211_EHT_PHY_CAP0_242_TONE_RU_GT20MHZ |
+ IEEE80211_EHT_PHY_CAP0_NDP_4_EHT_LFT_32_GI |
+ IEEE80211_EHT_PHY_CAP0_PARTIAL_BW_UL_MU_MIMO |
+ IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMEE |
+ IEEE80211_EHT_PHY_CAP0_BEAMFORMEE_SS_80MHZ_MASK,
+ .phy_cap_info[1] =
+ IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_80MHZ_MASK |
+ IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_160MHZ_MASK |
+ IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_320MHZ_MASK,
+ .phy_cap_info[3] =
+ IEEE80211_EHT_PHY_CAP3_NG_16_SU_FEEDBACK |
+ IEEE80211_EHT_PHY_CAP3_NG_16_MU_FEEDBACK |
+ IEEE80211_EHT_PHY_CAP3_CODEBOOK_4_2_SU_FDBK |
+ IEEE80211_EHT_PHY_CAP3_CODEBOOK_7_5_MU_FDBK |
+ IEEE80211_EHT_PHY_CAP3_TRIG_SU_BF_FDBK |
+ IEEE80211_EHT_PHY_CAP3_TRIG_MU_BF_PART_BW_FDBK |
+ IEEE80211_EHT_PHY_CAP3_TRIG_CQI_FDBK,
+
+ .phy_cap_info[4] =
+ IEEE80211_EHT_PHY_CAP4_PART_BW_DL_MU_MIMO |
+ IEEE80211_EHT_PHY_CAP4_POWER_BOOST_FACT_SUPP |
+ IEEE80211_EHT_PHY_CAP4_EHT_MU_PPDU_4_EHT_LTF_08_GI,
+ .phy_cap_info[5] =
+ IEEE80211_EHT_PHY_CAP5_NON_TRIG_CQI_FEEDBACK |
+ IEEE80211_EHT_PHY_CAP5_TX_LESS_242_TONE_RU_SUPP |
+ IEEE80211_EHT_PHY_CAP5_RX_LESS_242_TONE_RU_SUPP |
+ IEEE80211_EHT_PHY_CAP5_PPE_THRESHOLD_PRESENT,
+ .phy_cap_info[6] =
+ IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_MASK |
+ IEEE80211_EHT_PHY_CAP6_EHT_DUP_6GHZ_SUPP,
+ .phy_cap_info[8] =
+ IEEE80211_EHT_PHY_CAP8_RX_1024QAM_WIDER_BW_DL_OFDMA |
+ IEEE80211_EHT_PHY_CAP8_RX_4096QAM_WIDER_BW_DL_OFDMA,
+ },
+
+ /* For all MCS and bandwidth, set 2 NSS for both Tx and
+ * Rx - note we don't set the only_20mhz, but due to this
+ * being a union, it gets set correctly anyway.
+ */
+ .eht_mcs_nss_supp = {
+ .bw._80 = {
+ .rx_tx_mcs9_max_nss = 0x22,
+ .rx_tx_mcs11_max_nss = 0x22,
+ .rx_tx_mcs13_max_nss = 0x22,
+ },
+ .bw._160 = {
+ .rx_tx_mcs9_max_nss = 0x22,
+ .rx_tx_mcs11_max_nss = 0x22,
+ .rx_tx_mcs13_max_nss = 0x22,
+ },
+ .bw._320 = {
+ .rx_tx_mcs9_max_nss = 0x22,
+ .rx_tx_mcs11_max_nss = 0x22,
+ .rx_tx_mcs13_max_nss = 0x22,
+ },
+ },
+
+ /*
+ * PPE thresholds for NSS = 2, and RU index bitmap set
+ * to 0xc.
+ */
+ .eht_ppe_thres = {0xc1, 0x0e, 0xe0 }
+ },
},
{
.types_mask = BIT(NL80211_IFTYPE_AP),
@@ -644,9 +712,6 @@ static const struct ieee80211_sband_iftype_data iwl_he_capa[] = {
IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8,
.mac_cap_info[3] =
IEEE80211_HE_MAC_CAP3_OMI_CONTROL,
- .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,
.phy_cap_info[1] =
IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD,
.phy_cap_info[2] =
@@ -687,6 +752,49 @@ static const struct ieee80211_sband_iftype_data iwl_he_capa[] = {
*/
.ppe_thres = {0x61, 0x1c, 0xc7, 0x71},
},
+ .eht_cap = {
+ .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,
+ .phy_cap_info[0] =
+ IEEE80211_EHT_PHY_CAP0_242_TONE_RU_GT20MHZ |
+ IEEE80211_EHT_PHY_CAP0_NDP_4_EHT_LFT_32_GI,
+ .phy_cap_info[5] =
+ IEEE80211_EHT_PHY_CAP5_PPE_THRESHOLD_PRESENT,
+ },
+
+ /* For all MCS and bandwidth, set 2 NSS for both Tx and
+ * Rx - note we don't set the only_20mhz, but due to this
+ * being a union, it gets set correctly anyway.
+ */
+ .eht_mcs_nss_supp = {
+ .bw._80 = {
+ .rx_tx_mcs9_max_nss = 0x22,
+ .rx_tx_mcs11_max_nss = 0x22,
+ .rx_tx_mcs13_max_nss = 0x22,
+ },
+ .bw._160 = {
+ .rx_tx_mcs9_max_nss = 0x22,
+ .rx_tx_mcs11_max_nss = 0x22,
+ .rx_tx_mcs13_max_nss = 0x22,
+ },
+ .bw._320 = {
+ .rx_tx_mcs9_max_nss = 0x22,
+ .rx_tx_mcs11_max_nss = 0x22,
+ .rx_tx_mcs13_max_nss = 0x22,
+ },
+ },
+
+ /*
+ * PPE thresholds for NSS = 2, and RU index bitmap set
+ * to 0xc.
+ */
+ .eht_ppe_thres = {0xc1, 0x0e, 0xe0 }
+ },
},
};
@@ -738,6 +846,7 @@ static void iwl_init_he_6ghz_capa(struct iwl_trans *trans,
static void
iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans,
+ struct iwl_nvm_data *data,
struct ieee80211_supported_band *sband,
struct ieee80211_sband_iftype_data *iftype_data,
u8 tx_chains, u8 rx_chains,
@@ -745,6 +854,9 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans,
{
bool is_ap = iftype_data->types_mask & BIT(NL80211_IFTYPE_AP);
+ if (!data->sku_cap_11be_enable || iwlwifi_mod_params.disable_11be)
+ iftype_data->eht_cap.has_eht = false;
+
/* Advertise an A-MPDU exponent extension based on
* operating band
*/
@@ -755,9 +867,30 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans,
iftype_data->he_cap.he_cap_elem.mac_cap_info[3] |=
IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_3;
- if (is_ap && iwlwifi_mod_params.nvm_file)
+ switch (sband->band) {
+ case NL80211_BAND_2GHZ:
iftype_data->he_cap.he_cap_elem.phy_cap_info[0] |=
- IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G;
+ iftype_data->eht_cap.eht_cap_elem.mac_cap_info[0] |=
+ u8_encode_bits(IEEE80211_EHT_MAC_CAP0_MAX_MPDU_LEN_11454,
+ IEEE80211_EHT_MAC_CAP0_MAX_MPDU_LEN_MASK);
+ break;
+ case NL80211_BAND_6GHZ:
+ if (!is_ap || iwlwifi_mod_params.nvm_file)
+ iftype_data->eht_cap.eht_cap_elem.phy_cap_info[0] |=
+ IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ;
+ fallthrough;
+ case NL80211_BAND_5GHZ:
+ iftype_data->he_cap.he_cap_elem.phy_cap_info[0] |=
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G;
+ if (!is_ap || iwlwifi_mod_params.nvm_file)
+ iftype_data->he_cap.he_cap_elem.phy_cap_info[0] |=
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
+ break;
+ default:
+ WARN_ON(1);
+ break;
+ }
if ((tx_chains & rx_chains) == ANT_AB) {
iftype_data->he_cap.he_cap_elem.phy_cap_info[2] |=
@@ -765,19 +898,44 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans,
iftype_data->he_cap.he_cap_elem.phy_cap_info[5] |=
IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_2 |
IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_2;
- if (!is_ap)
+ if (!is_ap) {
iftype_data->he_cap.he_cap_elem.phy_cap_info[7] |=
IEEE80211_HE_PHY_CAP7_MAX_NC_2;
- } else if (!is_ap) {
- /* If not 2x2, we need to indicate 1x1 in the
- * Midamble RX Max NSTS - but not for AP mode
- */
- iftype_data->he_cap.he_cap_elem.phy_cap_info[1] &=
- ~IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS;
- iftype_data->he_cap.he_cap_elem.phy_cap_info[2] &=
- ~IEEE80211_HE_PHY_CAP2_MIDAMBLE_RX_TX_MAX_NSTS;
- iftype_data->he_cap.he_cap_elem.phy_cap_info[7] |=
- IEEE80211_HE_PHY_CAP7_MAX_NC_1;
+
+ if (iftype_data->eht_cap.has_eht) {
+ /*
+ * Set the number of sounding dimensions for each
+ * bandwidth to 1 to indicate the maximal supported
+ * value of TXVECTOR parameter NUM_STS of 2
+ */
+ iftype_data->eht_cap.eht_cap_elem.phy_cap_info[2] |= 0x49;
+
+ /*
+ * Set the MAX NC to 1 to indicate sounding feedback of
+ * 2 supported by the beamfomee.
+ */
+ iftype_data->eht_cap.eht_cap_elem.phy_cap_info[4] |= 0x10;
+ }
+ }
+ } else {
+ if (iftype_data->eht_cap.has_eht) {
+ struct ieee80211_eht_mcs_nss_supp *mcs_nss =
+ &iftype_data->eht_cap.eht_mcs_nss_supp;
+
+ memset(mcs_nss, 0x11, sizeof(*mcs_nss));
+ }
+
+ if (!is_ap) {
+ /* If not 2x2, we need to indicate 1x1 in the
+ * Midamble RX Max NSTS - but not for AP mode
+ */
+ iftype_data->he_cap.he_cap_elem.phy_cap_info[1] &=
+ ~IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS;
+ iftype_data->he_cap.he_cap_elem.phy_cap_info[2] &=
+ ~IEEE80211_HE_PHY_CAP2_MIDAMBLE_RX_TX_MAX_NSTS;
+ iftype_data->he_cap.he_cap_elem.phy_cap_info[7] |=
+ IEEE80211_HE_PHY_CAP7_MAX_NC_1;
+ }
}
switch (CSR_HW_RFID_TYPE(trans->hw_rf_id)) {
@@ -792,6 +950,29 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans,
break;
}
+ if (CSR_HW_REV_TYPE(trans->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_EPCS_PRIO_ACCESS |
+ IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE1 |
+ IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE2);
+ iftype_data->eht_cap.eht_cap_elem.phy_cap_info[3] &=
+ ~(IEEE80211_EHT_PHY_CAP0_PARTIAL_BW_UL_MU_MIMO |
+ IEEE80211_EHT_PHY_CAP3_NG_16_SU_FEEDBACK |
+ IEEE80211_EHT_PHY_CAP3_NG_16_MU_FEEDBACK |
+ IEEE80211_EHT_PHY_CAP3_CODEBOOK_4_2_SU_FDBK |
+ IEEE80211_EHT_PHY_CAP3_CODEBOOK_7_5_MU_FDBK |
+ IEEE80211_EHT_PHY_CAP3_TRIG_MU_BF_PART_BW_FDBK);
+ iftype_data->eht_cap.eht_cap_elem.phy_cap_info[4] &=
+ ~(IEEE80211_EHT_PHY_CAP4_PART_BW_DL_MU_MIMO |
+ IEEE80211_EHT_PHY_CAP4_POWER_BOOST_FACT_SUPP);
+ iftype_data->eht_cap.eht_cap_elem.phy_cap_info[5] &=
+ ~IEEE80211_EHT_PHY_CAP5_NON_TRIG_CQI_FEEDBACK;
+ iftype_data->eht_cap.eht_cap_elem.phy_cap_info[6] &=
+ ~(IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_MASK |
+ IEEE80211_EHT_PHY_CAP6_EHT_DUP_6GHZ_SUPP);
+ }
+
if (fw_has_capa(&fw->ucode_capa, IWL_UCODE_TLV_CAPA_BROADCAST_TWT))
iftype_data->he_cap.he_cap_elem.mac_cap_info[2] |=
IEEE80211_HE_MAC_CAP2_BCAST_TWT;
@@ -816,8 +997,8 @@ static void iwl_init_he_hw_capab(struct iwl_trans *trans,
if (WARN_ON(sband->iftype_data))
return;
- BUILD_BUG_ON(sizeof(data->iftd.low) != sizeof(iwl_he_capa));
- BUILD_BUG_ON(sizeof(data->iftd.high) != sizeof(iwl_he_capa));
+ BUILD_BUG_ON(sizeof(data->iftd.low) != sizeof(iwl_he_eht_capa));
+ BUILD_BUG_ON(sizeof(data->iftd.high) != sizeof(iwl_he_eht_capa));
switch (sband->band) {
case NL80211_BAND_2GHZ:
@@ -832,13 +1013,13 @@ static void iwl_init_he_hw_capab(struct iwl_trans *trans,
return;
}
- memcpy(iftype_data, iwl_he_capa, sizeof(iwl_he_capa));
+ memcpy(iftype_data, iwl_he_eht_capa, sizeof(iwl_he_eht_capa));
sband->iftype_data = iftype_data;
- sband->n_iftype_data = ARRAY_SIZE(iwl_he_capa);
+ sband->n_iftype_data = ARRAY_SIZE(iwl_he_eht_capa);
for (i = 0; i < sband->n_iftype_data; i++)
- iwl_nvm_fixup_sband_iftd(trans, sband, &iftype_data[i],
+ iwl_nvm_fixup_sband_iftd(trans, data, sband, &iftype_data[i],
tx_chains, rx_chains, fw);
iwl_init_he_6ghz_capa(trans, data, sband, tx_chains, rx_chains);
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h
index 82cf904e0b6d..62ce116d3783 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h
@@ -368,6 +368,7 @@ enum {
#define CNVR_AUX_MISC_CHIP 0xA2B800
#define CNVR_SCU_SD_REGS_SD_REG_DIG_DCDC_VTRIM 0xA29890
#define CNVR_SCU_SD_REGS_SD_REG_ACTIVE_VDIG_MIRROR 0xA29938
+#define CNVI_SCU_SEQ_DATA_DW9 0xA27488
#define PREG_AUX_BUS_WPROT_0 0xA04CC0
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h
index d659ccd065f7..32bd7f19f1d5 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h
@@ -1542,5 +1542,6 @@ void iwl_trans_free(struct iwl_trans *trans);
******************************************************/
int __must_check iwl_pci_register_driver(void);
void iwl_pci_unregister_driver(void);
+void iwl_trans_pcie_remove(struct iwl_trans *trans, bool rescan);
#endif /* __iwl_trans_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
index ef43f6971cd9..0a41cd0ab243 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
@@ -354,6 +354,20 @@ 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 ==
+ IWL_DEVICE_FAMILY_AX210) {
+ /* print these registers regardless of alive fail/success */
+ IWL_INFO(mvm, "WFPM_UMAC_PD_NOTIFICATION: 0x%x\n",
+ iwl_read_umac_prph(mvm->trans, WFPM_ARC1_PD_NOTIFICATION));
+ IWL_INFO(mvm, "WFPM_LMAC2_PD_NOTIFICATION: 0x%x\n",
+ iwl_read_umac_prph(mvm->trans, WFPM_LMAC2_PD_NOTIFICATION));
+ IWL_INFO(mvm, "WFPM_AUTH_KEY_0: 0x%x\n",
+ iwl_read_umac_prph(mvm->trans, SB_MODIFY_CFG_FLAG));
+ IWL_INFO(mvm, "CNVI_SCU_SEQ_DATA_DW9: 0x%x\n",
+ iwl_read_prph(mvm->trans, CNVI_SCU_SEQ_DATA_DW9));
+ }
+
if (ret) {
struct iwl_trans *trans = mvm->trans;
@@ -390,7 +404,7 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm,
UREG_LMAC2_CURRENT_PC));
}
- if (ret == -ETIMEDOUT)
+ if (ret == -ETIMEDOUT && !mvm->pldr_sync)
iwl_fw_dbg_error_collect(&mvm->fwrt,
FW_DBG_TRIGGER_ALIVE_TIMEOUT);
@@ -404,7 +418,8 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm,
return -EIO;
}
- iwl_mei_alive_notif(!ret);
+ /* if reached this point, Alive notification was received */
+ iwl_mei_alive_notif(true);
ret = iwl_pnvm_load(mvm->trans, &mvm->notif_wait);
if (ret) {
@@ -1467,18 +1482,22 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
return ret;
sb_cfg = iwl_read_umac_prph(mvm->trans, SB_MODIFY_CFG_FLAG);
- if (!(sb_cfg & SB_CFG_RESIDES_IN_OTP_MASK) && iwl_mei_pldr_req())
- return ret;
+ mvm->pldr_sync = !(sb_cfg & SB_CFG_RESIDES_IN_OTP_MASK);
+ if (mvm->pldr_sync && iwl_mei_pldr_req())
+ return -EBUSY;
ret = iwl_mvm_load_rt_fw(mvm);
if (ret) {
IWL_ERR(mvm, "Failed to start RT ucode: %d\n", ret);
- if (ret != -ERFKILL)
+ if (ret != -ERFKILL && !mvm->pldr_sync)
iwl_fw_dbg_error_collect(&mvm->fwrt,
FW_DBG_TRIGGER_DRIVER);
goto error;
}
+ /* FW loaded successfully */
+ mvm->pldr_sync = false;
+
iwl_get_shared_mem_conf(&mvm->fwrt);
ret = iwl_mvm_sf_update(mvm, NULL, false);
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
index a81dd488caa8..5273ade71117 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
@@ -1068,6 +1068,16 @@ static int iwl_mvm_mac_start(struct ieee80211_hw *hw)
if (!ret)
break;
+ /*
+ * In PLDR sync PCI re-enumeration is needed. no point to retry
+ * mac start before that.
+ */
+ if (mvm->pldr_sync) {
+ iwl_mei_alive_notif(false);
+ iwl_trans_pcie_remove(mvm->trans, true);
+ break;
+ }
+
IWL_ERR(mvm, "mac start retry %d\n", retry);
}
clear_bit(IWL_MVM_STATUS_STARTING, &mvm->status);
@@ -1825,7 +1835,8 @@ static u8 iwl_mvm_he_get_ppe_val(u8 *ppe, u8 ppe_pos_bit)
static void iwl_mvm_parse_ppe(struct iwl_mvm *mvm,
struct iwl_he_pkt_ext_v2 *pkt_ext, u8 nss,
- u8 ru_index_bitmap, u8 *ppe, u8 ppe_pos_bit)
+ u8 ru_index_bitmap, u8 *ppe, u8 ppe_pos_bit,
+ bool inheritance)
{
int i;
@@ -1851,14 +1862,25 @@ static void iwl_mvm_parse_ppe(struct iwl_mvm *mvm,
bw++) {
ru_index_tmp >>= 1;
- if (!(ru_index_tmp & 1))
- continue;
+ /*
+ * 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_mvm_he_get_ppe_val(ppe, ppe_pos_bit);
ppe_pos_bit += IEEE80211_PPE_THRES_INFO_PPET_SIZE;
low_th = iwl_mvm_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;
}
@@ -1867,7 +1889,8 @@ static void iwl_mvm_parse_ppe(struct iwl_mvm *mvm,
static void iwl_mvm_set_pkt_ext_from_he_ppe(struct iwl_mvm *mvm,
struct ieee80211_sta *sta,
- struct iwl_he_pkt_ext_v2 *pkt_ext)
+ struct iwl_he_pkt_ext_v2 *pkt_ext,
+ bool inheritance)
{
u8 nss = (sta->deflink.he_cap.ppe_thres[0] & IEEE80211_PPE_THRES_NSS_MASK) + 1;
u8 *ppe = &sta->deflink.he_cap.ppe_thres[0];
@@ -1877,7 +1900,8 @@ static void iwl_mvm_set_pkt_ext_from_he_ppe(struct iwl_mvm *mvm,
/* Starting after PPE header */
u8 ppe_pos_bit = IEEE80211_HE_PPE_THRES_INFO_HEADER_SIZE;
- iwl_mvm_parse_ppe(mvm, pkt_ext, nss, ru_index_bitmap, ppe, ppe_pos_bit);
+ iwl_mvm_parse_ppe(mvm, pkt_ext, nss, ru_index_bitmap, ppe, ppe_pos_bit,
+ inheritance);
}
static void iwl_mvm_set_pkt_ext_from_nominal_padding(struct iwl_he_pkt_ext_v2 *pkt_ext,
@@ -1888,16 +1912,18 @@ static void iwl_mvm_set_pkt_ext_from_nominal_padding(struct iwl_he_pkt_ext_v2 *p
int high_th = -1;
int i;
+ /* all the macros are the same for EHT and HE */
switch (nominal_padding) {
- case IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_0US:
+ 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_HE_PHY_CAP9_NOMINAL_PKT_PADDING_8US:
+ 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_HE_PHY_CAP9_NOMINAL_PKT_PADDING_16US:
+ 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;
@@ -1920,6 +1946,31 @@ static void iwl_mvm_set_pkt_ext_from_nominal_padding(struct iwl_he_pkt_ext_v2 *p
}
}
+static void iwl_mvm_get_optimal_ppe_info(struct iwl_he_pkt_ext_v2 *pkt_ext,
+ u8 nominal_padding)
+{
+ int i;
+
+ for (i = 0; i < MAX_HE_SUPP_NSS; i++) {
+ u8 bw;
+
+ for (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_mvm_cfg_he_sta(struct iwl_mvm *mvm,
struct ieee80211_vif *vif, u8 sta_id)
{
@@ -1943,6 +1994,7 @@ static void iwl_mvm_cfg_he_sta(struct iwl_mvm *mvm,
struct ieee80211_chanctx_conf *chanctx_conf;
const struct ieee80211_supported_band *sband;
void *cmd;
+ u8 nominal_padding;
if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_MBSSID_HE))
ver = 1;
@@ -2032,22 +2084,96 @@ static void iwl_mvm_cfg_he_sta(struct iwl_mvm *mvm,
memset(&sta_ctxt_cmd.pkt_ext, IWL_HE_PKT_EXT_NONE,
sizeof(sta_ctxt_cmd.pkt_ext));
- /* If PPE Thresholds exist, parse them into a FW-familiar format. */
- if (sta->deflink.he_cap.he_cap_elem.phy_cap_info[6] &
- IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) {
- iwl_mvm_set_pkt_ext_from_he_ppe(mvm, sta,
- &sta_ctxt_cmd.pkt_ext);
- flags |= STA_CTXT_HE_PACKET_EXT;
- /* PPE Thresholds doesn't exist - set the API PPE values
- * according to Common Nominal Packet Padding fiels. */
- } else {
- u8 nominal_padding =
- u8_get_bits(sta->deflink.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)
+ if (sta->deflink.eht_cap.has_eht) {
+ nominal_padding =
+ u8_get_bits(sta->deflink.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 (sta->deflink.eht_cap.eht_cap_elem.phy_cap_info[5] &
+ IEEE80211_EHT_PHY_CAP5_PPE_THRESHOLD_PRESENT) {
+ u8 nss = (sta->deflink.eht_cap.eht_ppe_thres[0] &
+ IEEE80211_EHT_PPE_THRES_NSS_MASK) + 1;
+ u8 *ppe = &sta->deflink.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_mvm_parse_ppe(mvm,
+ &sta_ctxt_cmd.pkt_ext,
+ nss, ru_index_bitmap, ppe,
+ ppe_pos_bit, true);
+ flags |= STA_CTXT_HE_PACKET_EXT;
+ /* EHT PPE Thresholds doesn't exist - set the API according to HE PPE Tresholds*/
+ } else if (sta->deflink.he_cap.he_cap_elem.phy_cap_info[6] &
+ IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) {
+ struct iwl_he_pkt_ext_v2 *pkt_ext =
+ &sta_ctxt_cmd.pkt_ext;
+
+ /*
+ * 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_mvm_set_pkt_ext_from_he_ppe(mvm, 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_mvm_get_optimal_ppe_info(pkt_ext, nominal_padding);
+
+ flags |= STA_CTXT_HE_PACKET_EXT;
+
+ /*
+ * if PPE Thresholds doesn't present in both EHT IE and HE IE -
+ * take the Thresholds from Common Nominal Packet Padding field
+ */
+ } else {
iwl_mvm_set_pkt_ext_from_nominal_padding(&sta_ctxt_cmd.pkt_ext,
nominal_padding,
&flags);
+ }
+ } else if (sta->deflink.he_cap.has_he) {
+ /* If PPE Thresholds exist, parse them into a FW-familiar format. */
+ if (sta->deflink.he_cap.he_cap_elem.phy_cap_info[6] &
+ IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) {
+ iwl_mvm_set_pkt_ext_from_he_ppe(mvm, sta,
+ &sta_ctxt_cmd.pkt_ext,
+ false);
+ flags |= STA_CTXT_HE_PACKET_EXT;
+ /*
+ * PPE Thresholds doesn't exist - set the API PPE values
+ * according to Common Nominal Packet Padding field.
+ */
+ } else {
+ nominal_padding =
+ u8_get_bits(sta->deflink.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_mvm_set_pkt_ext_from_nominal_padding(&sta_ctxt_cmd.pkt_ext,
+ nominal_padding,
+ &flags);
+ }
+ }
+
+ for (i = 0; i < MAX_HE_SUPP_NSS; i++) {
+ int bw;
+
+ for (bw = 0;
+ bw < ARRAY_SIZE(sta_ctxt_cmd.pkt_ext.pkt_ext_qam_th[i]);
+ bw++) {
+ u8 *qam_th =
+ &sta_ctxt_cmd.pkt_ext.pkt_ext_qam_th[i][bw][0];
+
+ IWL_DEBUG_HT(mvm,
+ "PPE table: nss[%d] bw[%d] PPET8 = %d, PPET16 = %d\n",
+ i, bw, qam_th[0], qam_th[1]);
+ }
}
if (sta->deflink.he_cap.he_cap_elem.mac_cap_info[2] &
@@ -2195,8 +2321,10 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
* interface was added.
*/
if (changes & BSS_CHANGED_ASSOC && vif->cfg.assoc) {
- if (vif->bss_conf.he_support &&
- !iwlwifi_mod_params.disable_11ax)
+ if ((vif->bss_conf.he_support &&
+ !iwlwifi_mod_params.disable_11ax) ||
+ (vif->bss_conf.eht_support &&
+ !iwlwifi_mod_params.disable_11be))
iwl_mvm_cfg_he_sta(mvm, vif, mvmvif->ap_sta_id);
iwl_mvm_mac_ctxt_recalc_tsf_id(mvm, vif);
@@ -2204,8 +2332,11 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
/* Update MU EDCA params */
if (changes & BSS_CHANGED_QOS && mvmvif->associated &&
- vif->cfg.assoc && vif->bss_conf.he_support &&
- !iwlwifi_mod_params.disable_11ax)
+ vif->cfg.assoc &&
+ ((vif->bss_conf.he_support &&
+ !iwlwifi_mod_params.disable_11ax) ||
+ (vif->bss_conf.eht_support &&
+ !iwlwifi_mod_params.disable_11be)))
iwl_mvm_cfg_he_sta(mvm, vif, mvmvif->ap_sta_id);
/*
@@ -3218,8 +3349,10 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
vif->bss_conf.he_support = sta->deflink.he_cap.has_he;
mvmvif->ap_assoc_sta_count++;
iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
- if (vif->bss_conf.he_support &&
- !iwlwifi_mod_params.disable_11ax)
+ if ((vif->bss_conf.he_support &&
+ !iwlwifi_mod_params.disable_11ax) ||
+ (vif->bss_conf.eht_support &&
+ !iwlwifi_mod_params.disable_11be))
iwl_mvm_cfg_he_sta(mvm, vif, mvm_sta->sta_id);
} else if (vif->type == NL80211_IFTYPE_STATION) {
vif->bss_conf.he_support = sta->deflink.he_cap.has_he;
@@ -3777,7 +3910,7 @@ static int iwl_mvm_send_aux_roc_cmd(struct iwl_mvm *mvm,
/* Set the channel info data */
iwl_mvm_set_chan_info(mvm, &aux_roc_req.channel_info, channel->hw_value,
iwl_mvm_phy_band_from_nl80211(channel->band),
- PHY_VHT_CHANNEL_MODE20,
+ IWL_PHY_CHANNEL_MODE20,
0);
/* Set the time and duration */
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
index 962e304fc2b1..ce6b701f3f4c 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
@@ -1105,6 +1105,8 @@ struct iwl_mvm {
unsigned long last_reset_or_resume_time_jiffies;
bool sta_remove_requires_queue_remove;
+
+ bool pldr_sync;
};
/* Extract MVM priv from op_mode and _hw */
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c
index 9699433137bc..e78f5beaa2d0 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c
@@ -1077,6 +1077,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
static const u8 no_reclaim_cmds[] = {
TX_CMD,
};
+ u32 max_agg;
size_t scan_size;
u32 min_backoff;
struct iwl_mvm_csme_conn_info *csme_conn_info __maybe_unused;
@@ -1098,12 +1099,17 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
if (!hw)
return NULL;
- hw->max_rx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF_HE;
+ if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ)
+ max_agg = IEEE80211_MAX_AMPDU_BUF_EHT;
+ 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 = IEEE80211_MAX_AMPDU_BUF_HE;
+ hw->max_tx_aggregation_subframes = max_agg;
op_mode = hw->priv;
@@ -1882,6 +1888,9 @@ static void iwl_mvm_nic_error(struct iwl_op_mode *op_mode, bool sync)
{
struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+ if (mvm->pldr_sync)
+ return;
+
if (!test_bit(STATUS_TRANS_DEAD, &mvm->trans->status) &&
!test_and_clear_bit(IWL_MVM_STATUS_SUPPRESS_ERROR_LOG_ONCE,
&mvm->status))
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c
index a3cefbc43e80..06f4203fb989 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c
@@ -14,16 +14,18 @@ u8 iwl_mvm_get_channel_width(struct cfg80211_chan_def *chandef)
switch (chandef->width) {
case NL80211_CHAN_WIDTH_20_NOHT:
case NL80211_CHAN_WIDTH_20:
- return PHY_VHT_CHANNEL_MODE20;
+ return IWL_PHY_CHANNEL_MODE20;
case NL80211_CHAN_WIDTH_40:
- return PHY_VHT_CHANNEL_MODE40;
+ return IWL_PHY_CHANNEL_MODE40;
case NL80211_CHAN_WIDTH_80:
- return PHY_VHT_CHANNEL_MODE80;
+ return IWL_PHY_CHANNEL_MODE80;
case NL80211_CHAN_WIDTH_160:
- return PHY_VHT_CHANNEL_MODE160;
+ return IWL_PHY_CHANNEL_MODE160;
+ case NL80211_CHAN_WIDTH_320:
+ return IWL_PHY_CHANNEL_MODE320;
default:
WARN(1, "Invalid channel width=%u", chandef->width);
- return PHY_VHT_CHANNEL_MODE20;
+ return IWL_PHY_CHANNEL_MODE20;
}
}
@@ -33,34 +35,32 @@ u8 iwl_mvm_get_channel_width(struct cfg80211_chan_def *chandef)
*/
u8 iwl_mvm_get_ctrl_pos(struct cfg80211_chan_def *chandef)
{
- switch (chandef->chan->center_freq - chandef->center_freq1) {
- case -70:
- return PHY_VHT_CTRL_POS_4_BELOW;
- case -50:
- return PHY_VHT_CTRL_POS_3_BELOW;
- case -30:
- return PHY_VHT_CTRL_POS_2_BELOW;
- case -10:
- return PHY_VHT_CTRL_POS_1_BELOW;
- case 10:
- return PHY_VHT_CTRL_POS_1_ABOVE;
- case 30:
- return PHY_VHT_CTRL_POS_2_ABOVE;
- case 50:
- return PHY_VHT_CTRL_POS_3_ABOVE;
- case 70:
- return PHY_VHT_CTRL_POS_4_ABOVE;
- default:
- WARN(1, "Invalid channel definition");
- fallthrough;
- case 0:
+ 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 PHY_VHT_CTRL_POS_1_BELOW;
+ 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;
}
/*
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c
index 2e9081cb6627..f30eeab5505b 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c
@@ -9,9 +9,11 @@
#include "iwl-op-mode.h"
#include "mvm.h"
-static u8 rs_fw_bw_from_sta_bw(struct ieee80211_sta *sta)
+static u8 rs_fw_bw_from_sta_bw(const struct ieee80211_sta *sta)
{
switch (sta->deflink.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:
@@ -238,6 +240,122 @@ rs_fw_he_set_enabled_rates(const struct ieee80211_sta *sta,
}
}
+static u8 rs_fw_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) \
+ rs_fw_eht_max_nss((rx)->rx_tx_mcs ##mcs_num## _max_nss, \
+ (tx)->rx_tx_mcs ##mcs_num## _max_nss)
+
+static void rs_fw_set_eht_mcs_nss(__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 *
+rs_fw_rs_mcs2eht_mcs(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 void rs_fw_eht_set_enabled_rates(const struct ieee80211_sta *sta,
+ struct ieee80211_supported_band *sband,
+ struct iwl_tlc_config_cmd_v4 *cmd)
+{
+ /* peer RX mcs capa */
+ const struct ieee80211_eht_mcs_nss_supp *eht_rx_mcs =
+ &sta->deflink.eht_cap.eht_mcs_nss_supp;
+ /* our TX mcs capa */
+ const struct ieee80211_eht_mcs_nss_supp *eht_tx_mcs =
+ &sband->iftype_data->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 20Mhz only */
+ if (!(sta->deflink.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 20Mhz only */
+ if (!(sband->iftype_data->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 bw */
+ bw = IWL_TLC_MCS_PER_BW_80;
+ rs_fw_set_eht_mcs_nss(cmd->ht_rates, bw,
+ MAX_NSS_MCS(7, &mcs_rx_20, &mcs_tx_20), GENMASK(7, 0));
+ rs_fw_set_eht_mcs_nss(cmd->ht_rates, bw,
+ MAX_NSS_MCS(9, &mcs_rx_20, &mcs_tx_20), GENMASK(9, 8));
+ rs_fw_set_eht_mcs_nss(cmd->ht_rates, bw,
+ MAX_NSS_MCS(11, &mcs_rx_20, &mcs_tx_20), GENMASK(11, 10));
+ rs_fw_set_eht_mcs_nss(cmd->ht_rates, bw,
+ MAX_NSS_MCS(13, &mcs_rx_20, &mcs_tx_20), GENMASK(13, 12));
+
+ /* rate for 160/320 bw */
+ 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 =
+ rs_fw_rs_mcs2eht_mcs(bw, eht_rx_mcs);
+ const struct ieee80211_eht_mcs_nss_supp_bw *mcs_tx =
+ rs_fw_rs_mcs2eht_mcs(bw, eht_tx_mcs);
+
+ /* got unsuppored index for bw */
+ if (!mcs_rx || !mcs_tx)
+ continue;
+
+ rs_fw_set_eht_mcs_nss(cmd->ht_rates, bw,
+ MAX_NSS_MCS(9, mcs_rx, mcs_tx), GENMASK(9, 0));
+ rs_fw_set_eht_mcs_nss(cmd->ht_rates, bw,
+ MAX_NSS_MCS(11, mcs_rx, mcs_tx), GENMASK(11, 10));
+ rs_fw_set_eht_mcs_nss(cmd->ht_rates, bw,
+ MAX_NSS_MCS(13, mcs_rx, mcs_tx), GENMASK(13, 12));
+ }
+
+ /* the station support only a single receive chain */
+ if (sta->deflink.smps_mode == IEEE80211_SMPS_STATIC ||
+ sta->deflink.rx_nss < 2)
+ memset(cmd->ht_rates[IWL_TLC_NSS_2], 0,
+ sizeof(cmd->ht_rates[IWL_TLC_NSS_2]));
+}
+
static void rs_fw_set_supp_rates(struct ieee80211_sta *sta,
struct ieee80211_supported_band *sband,
struct iwl_tlc_config_cmd_v4 *cmd)
@@ -258,7 +376,10 @@ static void rs_fw_set_supp_rates(struct ieee80211_sta *sta,
cmd->mode = IWL_TLC_MNG_MODE_NON_HT;
/* HT/VHT rates */
- if (he_cap->has_he) {
+ if (sta->deflink.eht_cap.has_eht) {
+ cmd->mode = IWL_TLC_MNG_MODE_EHT;
+ rs_fw_eht_set_enabled_rates(sta, sband, cmd);
+ } else if (he_cap->has_he) {
cmd->mode = IWL_TLC_MNG_MODE_HE;
rs_fw_he_set_enabled_rates(sta, sband, cmd);
} else if (vht_cap->vht_supported) {
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c
index f460332333a7..fadaa683a416 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c
@@ -1215,6 +1215,7 @@ int iwl_mvm_tx_skb_sta(struct iwl_mvm *mvm, struct sk_buff *skb,
struct sk_buff_head mpdus_skbs;
unsigned int payload_len;
int ret;
+ struct sk_buff *orig_skb = skb;
if (WARN_ON_ONCE(!mvmsta))
return -1;
@@ -1247,8 +1248,17 @@ int iwl_mvm_tx_skb_sta(struct iwl_mvm *mvm, struct sk_buff *skb,
ret = iwl_mvm_tx_mpdu(mvm, skb, &info, sta);
if (ret) {
+ /* Free skbs created as part of TSO logic that have not yet been dequeued */
__skb_queue_purge(&mpdus_skbs);
- return ret;
+ /* skb here is not necessarily same as skb that entered this method,
+ * so free it explicitly.
+ */
+ if (skb == orig_skb)
+ ieee80211_free_txskb(mvm->hw, skb);
+ else
+ kfree_skb(skb);
+ /* there was error, but we consumed skb one way or another, so return 0 */
+ return 0;
}
}
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c
index bd50f52a1aad..0a9af1ad1f20 100644
--- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c
@@ -2052,6 +2052,7 @@ static void iwl_trans_pcie_set_pmi(struct iwl_trans *trans, bool state)
struct iwl_trans_pcie_removal {
struct pci_dev *pdev;
struct work_struct work;
+ bool rescan;
};
static void iwl_trans_pcie_removal_wk(struct work_struct *wk)
@@ -2060,18 +2061,61 @@ static void iwl_trans_pcie_removal_wk(struct work_struct *wk)
container_of(wk, struct iwl_trans_pcie_removal, work);
struct pci_dev *pdev = removal->pdev;
static char *prop[] = {"EVENT=INACCESSIBLE", NULL};
+ struct pci_bus *bus = pdev->bus;
dev_err(&pdev->dev, "Device gone - attempting removal\n");
kobject_uevent_env(&pdev->dev.kobj, KOBJ_CHANGE, prop);
pci_lock_rescan_remove();
pci_dev_put(pdev);
pci_stop_and_remove_bus_device(pdev);
+ if (removal->rescan)
+ pci_rescan_bus(bus->parent);
pci_unlock_rescan_remove();
kfree(removal);
module_put(THIS_MODULE);
}
+void iwl_trans_pcie_remove(struct iwl_trans *trans, bool rescan)
+{
+ struct iwl_trans_pcie_removal *removal;
+
+ if (test_bit(STATUS_TRANS_DEAD, &trans->status))
+ return;
+
+ IWL_ERR(trans, "Device gone - scheduling removal!\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;
+ }
+
+ removal = kzalloc(sizeof(*removal), GFP_ATOMIC);
+ if (!removal) {
+ module_put(THIS_MODULE);
+ return;
+ }
+ /*
+ * we don't need to clear this flag, because
+ * the trans will be freed and reallocated.
+ */
+ set_bit(STATUS_TRANS_DEAD, &trans->status);
+
+ removal->pdev = to_pci_dev(trans->dev);
+ removal->rescan = rescan;
+ INIT_WORK(&removal->work, iwl_trans_pcie_removal_wk);
+ pci_dev_get(removal->pdev);
+ schedule_work(&removal->work);
+}
+EXPORT_SYMBOL(iwl_trans_pcie_remove);
+
/*
* This version doesn't disable BHs but rather assumes they're
* already disabled.
@@ -2131,47 +2175,12 @@ bool __iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans)
iwl_trans_pcie_dump_regs(trans);
- if (iwlwifi_mod_params.remove_when_gone && cntrl == ~0U) {
- struct iwl_trans_pcie_removal *removal;
-
- if (test_bit(STATUS_TRANS_DEAD, &trans->status))
- goto err;
-
- IWL_ERR(trans, "Device gone - scheduling removal!\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");
- goto err;
- }
-
- removal = kzalloc(sizeof(*removal), GFP_ATOMIC);
- if (!removal) {
- module_put(THIS_MODULE);
- goto err;
- }
- /*
- * we don't need to clear this flag, because
- * the trans will be freed and reallocated.
- */
- set_bit(STATUS_TRANS_DEAD, &trans->status);
-
- removal->pdev = to_pci_dev(trans->dev);
- INIT_WORK(&removal->work, iwl_trans_pcie_removal_wk);
- pci_dev_get(removal->pdev);
- schedule_work(&removal->work);
- } else {
+ if (iwlwifi_mod_params.remove_when_gone && cntrl == ~0U)
+ iwl_trans_pcie_remove(trans, false);
+ else
iwl_write32(trans, CSR_RESET,
CSR_RESET_REG_FLAG_FORCE_NMI);
- }
-err:
spin_unlock(&trans_pcie->reg_lock);
return false;
}
diff --git a/drivers/net/wireless/intersil/p54/eeprom.h b/drivers/net/wireless/intersil/p54/eeprom.h
index 1d0aaf54389a..641c4e79879e 100644
--- a/drivers/net/wireless/intersil/p54/eeprom.h
+++ b/drivers/net/wireless/intersil/p54/eeprom.h
@@ -108,10 +108,10 @@ struct pda_country {
} __packed;
struct pda_antenna_gain {
- struct {
+ DECLARE_FLEX_ARRAY(struct {
u8 gain_5GHz; /* 0.25 dBi units */
u8 gain_2GHz; /* 0.25 dBi units */
- } __packed antenna[0];
+ } __packed, antenna);
} __packed;
struct pda_custom_wrapper {
diff --git a/drivers/net/wireless/mediatek/mt76/Kconfig b/drivers/net/wireless/mediatek/mt76/Kconfig
index 9ff43f1fc50d..d7f90a0eb21e 100644
--- a/drivers/net/wireless/mediatek/mt76/Kconfig
+++ b/drivers/net/wireless/mediatek/mt76/Kconfig
@@ -34,3 +34,4 @@ source "drivers/net/wireless/mediatek/mt76/mt7603/Kconfig"
source "drivers/net/wireless/mediatek/mt76/mt7615/Kconfig"
source "drivers/net/wireless/mediatek/mt76/mt7915/Kconfig"
source "drivers/net/wireless/mediatek/mt76/mt7921/Kconfig"
+source "drivers/net/wireless/mediatek/mt76/mt7996/Kconfig"
diff --git a/drivers/net/wireless/mediatek/mt76/Makefile b/drivers/net/wireless/mediatek/mt76/Makefile
index c78ae4b89761..84c99b7e57f9 100644
--- a/drivers/net/wireless/mediatek/mt76/Makefile
+++ b/drivers/net/wireless/mediatek/mt76/Makefile
@@ -35,3 +35,4 @@ obj-$(CONFIG_MT7603E) += mt7603/
obj-$(CONFIG_MT7615_COMMON) += mt7615/
obj-$(CONFIG_MT7915E) += mt7915/
obj-$(CONFIG_MT7921_COMMON) += mt7921/
+obj-$(CONFIG_MT7996E) += mt7996/
diff --git a/drivers/net/wireless/mediatek/mt76/debugfs.c b/drivers/net/wireless/mediatek/mt76/debugfs.c
index 47e9911ee9fe..11b0b3d62f29 100644
--- a/drivers/net/wireless/mediatek/mt76/debugfs.c
+++ b/drivers/net/wireless/mediatek/mt76/debugfs.c
@@ -100,23 +100,6 @@ void mt76_seq_puts_array(struct seq_file *file, const char *str,
}
EXPORT_SYMBOL_GPL(mt76_seq_puts_array);
-static int mt76_read_rate_txpower(struct seq_file *s, void *data)
-{
- struct mt76_dev *dev = dev_get_drvdata(s->private);
-
- mt76_seq_puts_array(s, "CCK", dev->rate_power.cck,
- ARRAY_SIZE(dev->rate_power.cck));
- mt76_seq_puts_array(s, "OFDM", dev->rate_power.ofdm,
- ARRAY_SIZE(dev->rate_power.ofdm));
- mt76_seq_puts_array(s, "STBC", dev->rate_power.stbc,
- ARRAY_SIZE(dev->rate_power.stbc));
- mt76_seq_puts_array(s, "HT", dev->rate_power.ht,
- ARRAY_SIZE(dev->rate_power.ht));
- mt76_seq_puts_array(s, "VHT", dev->rate_power.vht,
- ARRAY_SIZE(dev->rate_power.vht));
- return 0;
-}
-
struct dentry *
mt76_register_debugfs_fops(struct mt76_phy *phy,
const struct file_operations *ops)
@@ -137,8 +120,6 @@ mt76_register_debugfs_fops(struct mt76_phy *phy,
debugfs_create_blob("eeprom", 0400, dir, &dev->eeprom);
if (dev->otp.data)
debugfs_create_blob("otp", 0400, dir, &dev->otp);
- debugfs_create_devm_seqfile(dev->dev, "rate_txpower", dir,
- mt76_read_rate_txpower);
debugfs_create_devm_seqfile(dev->dev, "rx-queues", dir,
mt76_rx_queues_read);
diff --git a/drivers/net/wireless/mediatek/mt76/dma.c b/drivers/net/wireless/mediatek/mt76/dma.c
index 28cbf07046c3..3f8c0845fcca 100644
--- a/drivers/net/wireless/mediatek/mt76/dma.c
+++ b/drivers/net/wireless/mediatek/mt76/dma.c
@@ -60,6 +60,19 @@ mt76_alloc_txwi(struct mt76_dev *dev)
}
static struct mt76_txwi_cache *
+mt76_alloc_rxwi(struct mt76_dev *dev)
+{
+ struct mt76_txwi_cache *t;
+
+ t = kzalloc(L1_CACHE_ALIGN(sizeof(*t)), GFP_ATOMIC);
+ if (!t)
+ return NULL;
+
+ t->ptr = NULL;
+ return t;
+}
+
+static struct mt76_txwi_cache *
__mt76_get_txwi(struct mt76_dev *dev)
{
struct mt76_txwi_cache *t = NULL;
@@ -76,6 +89,22 @@ __mt76_get_txwi(struct mt76_dev *dev)
}
static struct mt76_txwi_cache *
+__mt76_get_rxwi(struct mt76_dev *dev)
+{
+ struct mt76_txwi_cache *t = NULL;
+
+ spin_lock(&dev->wed_lock);
+ if (!list_empty(&dev->rxwi_cache)) {
+ t = list_first_entry(&dev->rxwi_cache, struct mt76_txwi_cache,
+ list);
+ list_del(&t->list);
+ }
+ spin_unlock(&dev->wed_lock);
+
+ return t;
+}
+
+static struct mt76_txwi_cache *
mt76_get_txwi(struct mt76_dev *dev)
{
struct mt76_txwi_cache *t = __mt76_get_txwi(dev);
@@ -86,6 +115,18 @@ mt76_get_txwi(struct mt76_dev *dev)
return mt76_alloc_txwi(dev);
}
+struct mt76_txwi_cache *
+mt76_get_rxwi(struct mt76_dev *dev)
+{
+ struct mt76_txwi_cache *t = __mt76_get_rxwi(dev);
+
+ if (t)
+ return t;
+
+ return mt76_alloc_rxwi(dev);
+}
+EXPORT_SYMBOL_GPL(mt76_get_rxwi);
+
void
mt76_put_txwi(struct mt76_dev *dev, struct mt76_txwi_cache *t)
{
@@ -98,6 +139,18 @@ mt76_put_txwi(struct mt76_dev *dev, struct mt76_txwi_cache *t)
}
EXPORT_SYMBOL_GPL(mt76_put_txwi);
+void
+mt76_put_rxwi(struct mt76_dev *dev, struct mt76_txwi_cache *t)
+{
+ if (!t)
+ return;
+
+ spin_lock(&dev->wed_lock);
+ list_add(&t->list, &dev->rxwi_cache);
+ spin_unlock(&dev->wed_lock);
+}
+EXPORT_SYMBOL_GPL(mt76_put_rxwi);
+
static void
mt76_free_pending_txwi(struct mt76_dev *dev)
{
@@ -113,6 +166,20 @@ mt76_free_pending_txwi(struct mt76_dev *dev)
}
static void
+mt76_free_pending_rxwi(struct mt76_dev *dev)
+{
+ struct mt76_txwi_cache *t;
+
+ local_bh_disable();
+ while ((t = __mt76_get_rxwi(dev)) != NULL) {
+ if (t->ptr)
+ skb_free_frag(t->ptr);
+ kfree(t);
+ }
+ local_bh_enable();
+}
+
+static void
mt76_dma_sync_idx(struct mt76_dev *dev, struct mt76_queue *q)
{
Q_WRITE(dev, q, desc_base, q->desc_dma);
@@ -148,11 +215,6 @@ mt76_dma_add_buf(struct mt76_dev *dev, struct mt76_queue *q,
u32 ctrl;
int i, idx = -1;
- if (txwi) {
- q->entry[q->head].txwi = DMA_DUMMY_DATA;
- q->entry[q->head].skip_buf0 = true;
- }
-
for (i = 0; i < nbufs; i += 2, buf += 2) {
u32 buf0 = buf[0].addr, buf1 = 0;
@@ -162,28 +224,48 @@ mt76_dma_add_buf(struct mt76_dev *dev, struct mt76_queue *q,
desc = &q->desc[idx];
entry = &q->entry[idx];
- if (buf[0].skip_unmap)
- entry->skip_buf0 = true;
- entry->skip_buf1 = i == nbufs - 1;
-
- entry->dma_addr[0] = buf[0].addr;
- entry->dma_len[0] = buf[0].len;
-
- ctrl = FIELD_PREP(MT_DMA_CTL_SD_LEN0, buf[0].len);
- if (i < nbufs - 1) {
- entry->dma_addr[1] = buf[1].addr;
- entry->dma_len[1] = buf[1].len;
- buf1 = buf[1].addr;
- ctrl |= FIELD_PREP(MT_DMA_CTL_SD_LEN1, buf[1].len);
- if (buf[1].skip_unmap)
- entry->skip_buf1 = true;
+ if ((q->flags & MT_QFLAG_WED) &&
+ FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_Q_RX) {
+ struct mt76_txwi_cache *t = txwi;
+ int rx_token;
+
+ if (!t)
+ return -ENOMEM;
+
+ rx_token = mt76_rx_token_consume(dev, (void *)skb, t,
+ buf[0].addr);
+ buf1 |= FIELD_PREP(MT_DMA_CTL_TOKEN, rx_token);
+ ctrl = FIELD_PREP(MT_DMA_CTL_SD_LEN0, buf[0].len) |
+ MT_DMA_CTL_TO_HOST;
+ } else {
+ if (txwi) {
+ q->entry[q->head].txwi = DMA_DUMMY_DATA;
+ q->entry[q->head].skip_buf0 = true;
+ }
+
+ if (buf[0].skip_unmap)
+ entry->skip_buf0 = true;
+ entry->skip_buf1 = i == nbufs - 1;
+
+ entry->dma_addr[0] = buf[0].addr;
+ entry->dma_len[0] = buf[0].len;
+
+ ctrl = FIELD_PREP(MT_DMA_CTL_SD_LEN0, buf[0].len);
+ if (i < nbufs - 1) {
+ entry->dma_addr[1] = buf[1].addr;
+ entry->dma_len[1] = buf[1].len;
+ buf1 = buf[1].addr;
+ ctrl |= FIELD_PREP(MT_DMA_CTL_SD_LEN1, buf[1].len);
+ if (buf[1].skip_unmap)
+ entry->skip_buf1 = true;
+ }
+
+ if (i == nbufs - 1)
+ ctrl |= MT_DMA_CTL_LAST_SEC0;
+ else if (i == nbufs - 2)
+ ctrl |= MT_DMA_CTL_LAST_SEC1;
}
- if (i == nbufs - 1)
- ctrl |= MT_DMA_CTL_LAST_SEC0;
- else if (i == nbufs - 2)
- ctrl |= MT_DMA_CTL_LAST_SEC1;
-
WRITE_ONCE(desc->buf0, cpu_to_le32(buf0));
WRITE_ONCE(desc->buf1, cpu_to_le32(buf1));
WRITE_ONCE(desc->info, cpu_to_le32(info));
@@ -272,33 +354,60 @@ mt76_dma_tx_cleanup(struct mt76_dev *dev, struct mt76_queue *q, bool flush)
static void *
mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx,
- int *len, u32 *info, bool *more)
+ int *len, u32 *info, bool *more, bool *drop)
{
struct mt76_queue_entry *e = &q->entry[idx];
struct mt76_desc *desc = &q->desc[idx];
- dma_addr_t buf_addr;
- void *buf = e->buf;
- int buf_len = SKB_WITH_OVERHEAD(q->buf_size);
+ void *buf;
- buf_addr = e->dma_addr[0];
if (len) {
- u32 ctl = le32_to_cpu(READ_ONCE(desc->ctrl));
- *len = FIELD_GET(MT_DMA_CTL_SD_LEN0, ctl);
- *more = !(ctl & MT_DMA_CTL_LAST_SEC0);
+ u32 ctrl = le32_to_cpu(READ_ONCE(desc->ctrl));
+ *len = FIELD_GET(MT_DMA_CTL_SD_LEN0, ctrl);
+ *more = !(ctrl & MT_DMA_CTL_LAST_SEC0);
}
if (info)
*info = le32_to_cpu(desc->info);
- dma_unmap_single(dev->dma_dev, buf_addr, buf_len, DMA_FROM_DEVICE);
- e->buf = NULL;
+ if ((q->flags & MT_QFLAG_WED) &&
+ FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_Q_RX) {
+ u32 token = FIELD_GET(MT_DMA_CTL_TOKEN,
+ le32_to_cpu(desc->buf1));
+ struct mt76_txwi_cache *t = mt76_rx_token_release(dev, token);
+
+ if (!t)
+ return NULL;
+
+ dma_unmap_single(dev->dma_dev, t->dma_addr,
+ SKB_WITH_OVERHEAD(q->buf_size),
+ DMA_FROM_DEVICE);
+
+ buf = t->ptr;
+ t->dma_addr = 0;
+ t->ptr = NULL;
+
+ mt76_put_rxwi(dev, t);
+
+ if (drop) {
+ u32 ctrl = le32_to_cpu(READ_ONCE(desc->ctrl));
+
+ *drop = !!(ctrl & (MT_DMA_CTL_TO_HOST_A |
+ MT_DMA_CTL_DROP));
+ }
+ } else {
+ buf = e->buf;
+ e->buf = NULL;
+ dma_unmap_single(dev->dma_dev, e->dma_addr[0],
+ SKB_WITH_OVERHEAD(q->buf_size),
+ DMA_FROM_DEVICE);
+ }
return buf;
}
static void *
mt76_dma_dequeue(struct mt76_dev *dev, struct mt76_queue *q, bool flush,
- int *len, u32 *info, bool *more)
+ int *len, u32 *info, bool *more, bool *drop)
{
int idx = q->tail;
@@ -314,7 +423,7 @@ mt76_dma_dequeue(struct mt76_dev *dev, struct mt76_queue *q, bool flush,
q->tail = (q->tail + 1) % q->ndesc;
q->queued--;
- return mt76_dma_get_buf(dev, q, idx, len, info, more);
+ return mt76_dma_get_buf(dev, q, idx, len, info, more, drop);
}
static int
@@ -441,14 +550,26 @@ free_skb:
return ret;
}
+static struct page_frag_cache *
+mt76_dma_rx_get_frag_cache(struct mt76_dev *dev, struct mt76_queue *q)
+{
+ struct page_frag_cache *rx_page = &q->rx_page;
+
+#ifdef CONFIG_NET_MEDIATEK_SOC_WED
+ if ((q->flags & MT_QFLAG_WED) &&
+ FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_Q_RX)
+ rx_page = &dev->mmio.wed.rx_buf_ring.rx_page;
+#endif
+ return rx_page;
+}
+
static int
mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q)
{
- dma_addr_t addr;
- void *buf;
- int frames = 0;
+ struct page_frag_cache *rx_page = mt76_dma_rx_get_frag_cache(dev, q);
int len = SKB_WITH_OVERHEAD(q->buf_size);
- int offset = q->buf_offset;
+ int frames = 0, offset = q->buf_offset;
+ dma_addr_t addr;
if (!q->ndesc)
return 0;
@@ -456,9 +577,18 @@ mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q)
spin_lock_bh(&q->lock);
while (q->queued < q->ndesc - 1) {
+ struct mt76_txwi_cache *t = NULL;
struct mt76_queue_buf qbuf;
+ void *buf = NULL;
- buf = page_frag_alloc(&q->rx_page, q->buf_size, GFP_ATOMIC);
+ if ((q->flags & MT_QFLAG_WED) &&
+ FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_Q_RX) {
+ t = mt76_get_rxwi(dev);
+ if (!t)
+ break;
+ }
+
+ buf = page_frag_alloc(rx_page, q->buf_size, GFP_ATOMIC);
if (!buf)
break;
@@ -471,7 +601,7 @@ mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q)
qbuf.addr = addr + offset;
qbuf.len = len - offset;
qbuf.skip_unmap = false;
- mt76_dma_add_buf(dev, q, &qbuf, 1, 0, buf, NULL);
+ mt76_dma_add_buf(dev, q, &qbuf, 1, 0, buf, t);
frames++;
}
@@ -517,6 +647,11 @@ mt76_dma_wed_setup(struct mt76_dev *dev, struct mt76_queue *q)
if (!ret)
q->wed_regs = wed->txfree_ring.reg_base;
break;
+ case MT76_WED_Q_RX:
+ ret = mtk_wed_device_rx_ring_setup(wed, ring, q->regs);
+ if (!ret)
+ q->wed_regs = wed->rx_ring[ring].reg_base;
+ break;
default:
ret = -EINVAL;
}
@@ -574,7 +709,7 @@ mt76_dma_rx_cleanup(struct mt76_dev *dev, struct mt76_queue *q)
spin_lock_bh(&q->lock);
do {
- buf = mt76_dma_dequeue(dev, q, true, NULL, NULL, &more);
+ buf = mt76_dma_dequeue(dev, q, true, NULL, NULL, &more, NULL);
if (!buf)
break;
@@ -615,7 +750,7 @@ mt76_dma_rx_reset(struct mt76_dev *dev, enum mt76_rxq_id qid)
static void
mt76_add_fragment(struct mt76_dev *dev, struct mt76_queue *q, void *data,
- int len, bool more)
+ int len, bool more, u32 info)
{
struct sk_buff *skb = q->rx_head;
struct skb_shared_info *shinfo = skb_shinfo(skb);
@@ -635,7 +770,7 @@ mt76_add_fragment(struct mt76_dev *dev, struct mt76_queue *q, void *data,
q->rx_head = NULL;
if (nr_frags < ARRAY_SIZE(shinfo->frags))
- dev->drv->rx_skb(dev, q - dev->q_rx, skb);
+ dev->drv->rx_skb(dev, q - dev->q_rx, skb, &info);
else
dev_kfree_skb(skb);
}
@@ -656,6 +791,7 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
}
while (done < budget) {
+ bool drop = false;
u32 info;
if (check_ddone) {
@@ -666,10 +802,14 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
break;
}
- data = mt76_dma_dequeue(dev, q, false, &len, &info, &more);
+ data = mt76_dma_dequeue(dev, q, false, &len, &info, &more,
+ &drop);
if (!data)
break;
+ if (drop)
+ goto free_frag;
+
if (q->rx_head)
data_len = q->buf_size;
else
@@ -682,7 +822,7 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
}
if (q->rx_head) {
- mt76_add_fragment(dev, q, data, len, more);
+ mt76_add_fragment(dev, q, data, len, more, info);
continue;
}
@@ -706,7 +846,7 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget)
continue;
}
- dev->drv->rx_skb(dev, q - dev->q_rx, skb);
+ dev->drv->rx_skb(dev, q - dev->q_rx, skb, &info);
continue;
free_frag:
@@ -803,11 +943,15 @@ void mt76_dma_cleanup(struct mt76_dev *dev)
mt76_dma_tx_cleanup(dev, dev->q_mcu[i], true);
mt76_for_each_q_rx(dev, i) {
+ struct mt76_queue *q = &dev->q_rx[i];
+
netif_napi_del(&dev->napi[i]);
- mt76_dma_rx_cleanup(dev, &dev->q_rx[i]);
+ if (FIELD_GET(MT_QFLAG_WED_TYPE, q->flags))
+ mt76_dma_rx_cleanup(dev, q);
}
mt76_free_pending_txwi(dev);
+ mt76_free_pending_rxwi(dev);
if (mtk_wed_device_active(&dev->mmio.wed))
mtk_wed_device_detach(&dev->mmio.wed);
diff --git a/drivers/net/wireless/mediatek/mt76/dma.h b/drivers/net/wireless/mediatek/mt76/dma.h
index fdf786f975ea..53c6ce2528b2 100644
--- a/drivers/net/wireless/mediatek/mt76/dma.h
+++ b/drivers/net/wireless/mediatek/mt76/dma.h
@@ -15,6 +15,14 @@
#define MT_DMA_CTL_SD_LEN0 GENMASK(29, 16)
#define MT_DMA_CTL_LAST_SEC0 BIT(30)
#define MT_DMA_CTL_DMA_DONE BIT(31)
+#define MT_DMA_CTL_TO_HOST BIT(8)
+#define MT_DMA_CTL_TO_HOST_A BIT(12)
+#define MT_DMA_CTL_DROP BIT(14)
+#define MT_DMA_CTL_TOKEN GENMASK(31, 16)
+
+#define MT_DMA_PPE_CPU_REASON GENMASK(15, 11)
+#define MT_DMA_PPE_ENTRY GENMASK(30, 16)
+#define MT_DMA_INFO_PPE_VLD BIT(31)
#define MT_DMA_HDR_LEN 4
#define MT_RX_INFO_LEN 4
diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c
index 6de13d641438..fc608b369b3c 100644
--- a/drivers/net/wireless/mediatek/mt76/mac80211.c
+++ b/drivers/net/wireless/mediatek/mt76/mac80211.c
@@ -443,8 +443,12 @@ mt76_phy_init(struct mt76_phy *phy, struct ieee80211_hw *hw)
ieee80211_hw_set(hw, SUPPORTS_CLONED_SKBS);
ieee80211_hw_set(hw, SUPPORTS_AMSDU_IN_AMPDU);
ieee80211_hw_set(hw, SUPPORTS_REORDERING_BUFFER);
- ieee80211_hw_set(hw, TX_AMSDU);
- ieee80211_hw_set(hw, TX_FRAG_LIST);
+
+ if (!(dev->drv->drv_flags & MT_DRV_AMSDU_OFFLOAD)) {
+ ieee80211_hw_set(hw, TX_AMSDU);
+ ieee80211_hw_set(hw, TX_FRAG_LIST);
+ }
+
ieee80211_hw_set(hw, MFP_CAPABLE);
ieee80211_hw_set(hw, AP_LINK_PS);
ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS);
@@ -568,6 +572,7 @@ mt76_alloc_device(struct device *pdev, unsigned int size,
spin_lock_init(&dev->lock);
spin_lock_init(&dev->cc_lock);
spin_lock_init(&dev->status_lock);
+ spin_lock_init(&dev->wed_lock);
mutex_init(&dev->mutex);
init_waitqueue_head(&dev->tx_wait);
@@ -590,9 +595,13 @@ mt76_alloc_device(struct device *pdev, unsigned int size,
spin_lock_init(&dev->token_lock);
idr_init(&dev->token);
+ spin_lock_init(&dev->rx_token_lock);
+ idr_init(&dev->rx_token);
+
INIT_LIST_HEAD(&dev->wcid_list);
INIT_LIST_HEAD(&dev->txwi_cache);
+ INIT_LIST_HEAD(&dev->rxwi_cache);
dev->token_size = dev->drv->token_size;
for (i = 0; i < ARRAY_SIZE(dev->q_rx); i++)
@@ -947,14 +956,12 @@ void mt76_wcid_key_setup(struct mt76_dev *dev, struct mt76_wcid *wcid,
}
EXPORT_SYMBOL(mt76_wcid_key_setup);
-static int
-mt76_rx_signal(struct mt76_rx_status *status)
+int mt76_rx_signal(u8 chain_mask, s8 *chain_signal)
{
- s8 *chain_signal = status->chain_signal;
int signal = -128;
u8 chains;
- for (chains = status->chains; chains; chains >>= 1, chain_signal++) {
+ for (chains = chain_mask; chains; chains >>= 1, chain_signal++) {
int cur, diff;
cur = *chain_signal;
@@ -976,6 +983,7 @@ mt76_rx_signal(struct mt76_rx_status *status)
return signal;
}
+EXPORT_SYMBOL(mt76_rx_signal);
static void
mt76_rx_convert(struct mt76_dev *dev, struct sk_buff *skb,
@@ -1005,7 +1013,7 @@ mt76_rx_convert(struct mt76_dev *dev, struct sk_buff *skb,
status->ampdu_reference = mstat.ampdu_ref;
status->device_timestamp = mstat.timestamp;
status->mactime = mstat.timestamp;
- status->signal = mt76_rx_signal(&mstat);
+ status->signal = mt76_rx_signal(mstat.chains, mstat.chain_signal);
if (status->signal <= -128)
status->flag |= RX_FLAG_NO_SIGNAL_VAL;
@@ -1289,7 +1297,10 @@ void mt76_rx_poll_complete(struct mt76_dev *dev, enum mt76_rxq_id q,
while ((skb = __skb_dequeue(&dev->rx_skb[q])) != NULL) {
mt76_check_sta(dev, skb);
- mt76_rx_aggr_reorder(skb, &frames);
+ if (mtk_wed_device_active(&dev->mmio.wed))
+ __skb_queue_tail(&frames, skb);
+ else
+ mt76_rx_aggr_reorder(skb, &frames);
}
mt76_rx_complete(dev, &frames, napi);
diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h
index 87db9498dea4..32a77a0ae9da 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76.h
@@ -35,6 +35,7 @@
FIELD_PREP(MT_QFLAG_WED_TYPE, _type) | \
FIELD_PREP(MT_QFLAG_WED_RING, _n))
#define MT_WED_Q_TX(_n) __MT_WED_Q(MT76_WED_Q_TX, _n)
+#define MT_WED_Q_RX(_n) __MT_WED_Q(MT76_WED_Q_RX, _n)
#define MT_WED_Q_TXFREE __MT_WED_Q(MT76_WED_Q_TXFREE, 0)
struct mt76_dev;
@@ -56,6 +57,7 @@ enum mt76_bus_type {
enum mt76_wed_type {
MT76_WED_Q_TX,
MT76_WED_Q_TXFREE,
+ MT76_WED_Q_RX,
};
struct mt76_bus_ops {
@@ -271,9 +273,15 @@ struct mt76_sta_stats {
u64 tx_nss[4]; /* 1, 2, 3, 4 */
u64 tx_mcs[16]; /* mcs idx */
u64 tx_bytes;
+ /* WED TX */
u32 tx_packets;
u32 tx_retries;
u32 tx_failed;
+ /* WED RX */
+ u64 rx_bytes;
+ u32 rx_packets;
+ u32 rx_errors;
+ u32 rx_drops;
};
enum mt76_wcid_flags {
@@ -339,7 +347,10 @@ struct mt76_txwi_cache {
struct list_head list;
dma_addr_t dma_addr;
- struct sk_buff *skb;
+ union {
+ struct sk_buff *skb;
+ void *ptr;
+ };
};
struct mt76_rx_tid {
@@ -415,6 +426,7 @@ struct mt76_hw_cap {
#define MT_DRV_SW_RX_AIRTIME BIT(2)
#define MT_DRV_RX_DMA_HDR BIT(3)
#define MT_DRV_HW_MGMT_TXQ BIT(4)
+#define MT_DRV_AMSDU_OFFLOAD BIT(5)
struct mt76_driver_ops {
u32 drv_flags;
@@ -438,7 +450,7 @@ struct mt76_driver_ops {
bool (*rx_check)(struct mt76_dev *dev, void *data, int len);
void (*rx_skb)(struct mt76_dev *dev, enum mt76_rxq_id q,
- struct sk_buff *skb);
+ struct sk_buff *skb, u32 *info);
void (*rx_poll_complete)(struct mt76_dev *dev, enum mt76_rxq_id q);
@@ -470,19 +482,6 @@ struct mt76_sband {
struct mt76_channel_state *chan;
};
-struct mt76_rate_power {
- union {
- struct {
- s8 cck[4];
- s8 ofdm[8];
- s8 stbc[10];
- s8 ht[16];
- s8 vht[10];
- };
- s8 all[48];
- };
-};
-
/* addr req mask */
#define MT_VEND_TYPE_EEPROM BIT(31)
#define MT_VEND_TYPE_CFG BIT(30)
@@ -705,6 +704,8 @@ struct mt76_phy {
enum mt76_dfs_state dfs_state;
ktime_t survey_time;
+ u32 aggr_stats[32];
+
struct mt76_hw_cap cap;
struct mt76_sband sband_2g;
struct mt76_sband sband_5g;
@@ -738,6 +739,7 @@ struct mt76_dev {
struct ieee80211_hw *hw;
+ spinlock_t wed_lock;
spinlock_t lock;
spinlock_t cc_lock;
@@ -764,6 +766,7 @@ struct mt76_dev {
struct sk_buff_head rx_skb[__MT_RXQ_MAX];
struct list_head txwi_cache;
+ struct list_head rxwi_cache;
struct mt76_queue *q_mcu[__MT_MCUQ_MAX];
struct mt76_queue q_rx[__MT_RXQ_MAX];
const struct mt76_queue_ops *queue_ops;
@@ -778,6 +781,10 @@ struct mt76_dev {
u16 token_count;
u16 token_size;
+ spinlock_t rx_token_lock;
+ struct idr rx_token;
+ u16 rx_token_size;
+
wait_queue_head_t tx_wait;
/* spinclock used to protect wcid pktid linked list */
spinlock_t status_lock;
@@ -793,8 +800,6 @@ struct mt76_dev {
u32 rev;
- u32 aggr_stats[32];
-
struct tasklet_struct pre_tbtt_tasklet;
int beacon_int;
u8 beacon_mask;
@@ -802,8 +807,6 @@ struct mt76_dev {
struct debugfs_blob_wrapper eeprom;
struct debugfs_blob_wrapper otp;
- struct mt76_rate_power rate_power;
-
char alpha2[3];
enum nl80211_dfs_regions region;
@@ -1107,8 +1110,9 @@ static inline bool mt76_is_skb_pktid(u8 pktid)
static inline u8 mt76_tx_power_nss_delta(u8 nss)
{
static const u8 nss_delta[4] = { 0, 6, 9, 12 };
+ u8 idx = nss - 1;
- return nss_delta[nss - 1];
+ return (idx < ARRAY_SIZE(nss_delta)) ? nss_delta[idx] : 0;
}
static inline bool mt76_testmode_enabled(struct mt76_phy *phy)
@@ -1163,6 +1167,7 @@ void mt76_update_survey(struct mt76_phy *phy);
void mt76_update_survey_active_time(struct mt76_phy *phy, ktime_t time);
int mt76_get_survey(struct ieee80211_hw *hw, int idx,
struct survey_info *survey);
+int mt76_rx_signal(u8 chain_mask, s8 *chain_signal);
void mt76_set_stream_caps(struct mt76_phy *phy, bool vht);
int mt76_rx_aggr_start(struct mt76_dev *dev, struct mt76_wcid *wcid, u8 tid,
@@ -1260,6 +1265,8 @@ mt76_tx_status_get_hw(struct mt76_dev *dev, struct sk_buff *skb)
}
void mt76_put_txwi(struct mt76_dev *dev, struct mt76_txwi_cache *t);
+void mt76_put_rxwi(struct mt76_dev *dev, struct mt76_txwi_cache *t);
+struct mt76_txwi_cache *mt76_get_rxwi(struct mt76_dev *dev);
void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames,
struct napi_struct *napi);
void mt76_rx_poll_complete(struct mt76_dev *dev, enum mt76_rxq_id q,
@@ -1404,6 +1411,9 @@ struct mt76_txwi_cache *
mt76_token_release(struct mt76_dev *dev, int token, bool *wake);
int mt76_token_consume(struct mt76_dev *dev, struct mt76_txwi_cache **ptxwi);
void __mt76_set_tx_blocked(struct mt76_dev *dev, bool blocked);
+struct mt76_txwi_cache *mt76_rx_token_release(struct mt76_dev *dev, int token);
+int mt76_rx_token_consume(struct mt76_dev *dev, void *ptr,
+ struct mt76_txwi_cache *r, dma_addr_t phys);
static inline void mt76_set_tx_blocked(struct mt76_dev *dev, bool blocked)
{
diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7603/debugfs.c
index f52165dff422..3967f2f05774 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7603/debugfs.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7603/debugfs.c
@@ -85,7 +85,7 @@ mt7603_ampdu_stat_show(struct seq_file *file, void *data)
bound[i], bound[i + 1]);
seq_puts(file, "\nCount: ");
for (i = 0; i < ARRAY_SIZE(bound); i++)
- seq_printf(file, "%8d | ", dev->mt76.aggr_stats[i]);
+ seq_printf(file, "%8d | ", dev->mphy.aggr_stats[i]);
seq_puts(file, "\n");
return 0;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/dma.c b/drivers/net/wireless/mediatek/mt76/mt7603/dma.c
index f9e5857850e7..03ba11a61c90 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7603/dma.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7603/dma.c
@@ -69,7 +69,7 @@ free:
}
void mt7603_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
- struct sk_buff *skb)
+ struct sk_buff *skb, u32 *info)
{
struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76);
__le32 *rxd = (__le32 *)skb->data;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/mac.c b/drivers/net/wireless/mediatek/mt76/mt7603/mac.c
index 49a511ae8161..70a7f84af028 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7603/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7603/mac.c
@@ -39,7 +39,7 @@ void mt7603_mac_reset_counters(struct mt7603_dev *dev)
for (i = 0; i < 2; i++)
mt76_rr(dev, MT_TX_AGG_CNT(i));
- memset(dev->mt76.aggr_stats, 0, sizeof(dev->mt76.aggr_stats));
+ memset(dev->mphy.aggr_stats, 0, sizeof(dev->mphy.aggr_stats));
}
void mt7603_mac_set_timing(struct mt7603_dev *dev)
@@ -1827,8 +1827,8 @@ void mt7603_mac_work(struct work_struct *work)
for (i = 0, idx = 0; i < 2; i++) {
u32 val = mt76_rr(dev, MT_TX_AGG_CNT(i));
- dev->mt76.aggr_stats[idx++] += val & 0xffff;
- dev->mt76.aggr_stats[idx++] += val >> 16;
+ dev->mphy.aggr_stats[idx++] += val & 0xffff;
+ dev->mphy.aggr_stats[idx++] += val >> 16;
}
if (dev->mphy.mac_work_count == 10)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/mt7603.h b/drivers/net/wireless/mediatek/mt76/mt7603/mt7603.h
index 0fd46d907638..7c3be596da09 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7603/mt7603.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7603/mt7603.h
@@ -244,7 +244,7 @@ int mt7603_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
void mt7603_tx_complete_skb(struct mt76_dev *mdev, struct mt76_queue_entry *e);
void mt7603_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
- struct sk_buff *skb);
+ struct sk_buff *skb, u32 *info);
void mt7603_rx_poll_complete(struct mt76_dev *mdev, enum mt76_rxq_id q);
void mt7603_sta_ps(struct mt76_dev *mdev, struct ieee80211_sta *sta, bool ps);
int mt7603_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7615/debugfs.c
index c26b45a09923..2a6d317db5e0 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/debugfs.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/debugfs.c
@@ -278,7 +278,6 @@ mt7615_ampdu_stat_read_phy(struct mt7615_phy *phy,
{
struct mt7615_dev *dev = file->private;
u32 reg = is_mt7663(&dev->mt76) ? MT_MIB_ARNG(0) : MT_AGG_ASRCR0;
- bool ext_phy = phy != &dev->phy;
int bound[7], i, range;
if (!phy)
@@ -292,7 +291,7 @@ mt7615_ampdu_stat_read_phy(struct mt7615_phy *phy,
for (i = 0; i < 3; i++)
bound[i + 4] = MT_AGG_ASRCR_RANGE(range, i) + 1;
- seq_printf(file, "\nPhy %d\n", ext_phy);
+ seq_printf(file, "\nPhy %d\n", phy != &dev->phy);
seq_printf(file, "Length: %8d | ", bound[0]);
for (i = 0; i < ARRAY_SIZE(bound) - 1; i++)
@@ -300,9 +299,8 @@ mt7615_ampdu_stat_read_phy(struct mt7615_phy *phy,
bound[i], bound[i + 1]);
seq_puts(file, "\nCount: ");
- range = ext_phy ? ARRAY_SIZE(dev->mt76.aggr_stats) / 2 : 0;
for (i = 0; i < ARRAY_SIZE(bound); i++)
- seq_printf(file, "%8d | ", dev->mt76.aggr_stats[i + range]);
+ seq_printf(file, "%8d | ", phy->mt76->aggr_stats[i]);
seq_puts(file, "\n");
seq_printf(file, "BA miss count: %d\n", phy->mib.ba_miss_cnt);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mac.c b/drivers/net/wireless/mediatek/mt76/mt7615/mac.c
index 2ce1705c0f43..a95602473359 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/mac.c
@@ -107,9 +107,9 @@ static struct mt76_wcid *mt7615_rx_get_wcid(struct mt7615_dev *dev,
return &sta->vif->sta.wcid;
}
-void mt7615_mac_reset_counters(struct mt7615_dev *dev)
+void mt7615_mac_reset_counters(struct mt7615_phy *phy)
{
- struct mt76_phy *mphy_ext = dev->mt76.phys[MT_BAND1];
+ struct mt7615_dev *dev = phy->dev;
int i;
for (i = 0; i < 4; i++) {
@@ -117,10 +117,8 @@ void mt7615_mac_reset_counters(struct mt7615_dev *dev)
mt76_rr(dev, MT_TX_AGG_CNT(1, i));
}
- memset(dev->mt76.aggr_stats, 0, sizeof(dev->mt76.aggr_stats));
- dev->mt76.phy.survey_time = ktime_get_boottime();
- if (mphy_ext)
- mphy_ext->survey_time = ktime_get_boottime();
+ memset(phy->mt76->aggr_stats, 0, sizeof(phy->mt76->aggr_stats));
+ phy->mt76->survey_time = ktime_get_boottime();
/* reset airtime counters */
mt76_rr(dev, MT_MIB_SDR9(0));
@@ -1177,6 +1175,21 @@ void mt7615_mac_set_rates(struct mt7615_phy *phy, struct mt7615_sta *sta,
}
EXPORT_SYMBOL_GPL(mt7615_mac_set_rates);
+void mt7615_mac_enable_rtscts(struct mt7615_dev *dev,
+ struct ieee80211_vif *vif, bool enable)
+{
+ struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv;
+ u32 addr;
+
+ addr = mt7615_mac_wtbl_addr(dev, mvif->sta.wcid.idx) + 3 * 4;
+
+ if (enable)
+ mt76_set(dev, addr, MT_WTBL_W3_RTS);
+ else
+ mt76_clear(dev, addr, MT_WTBL_W3_RTS);
+}
+EXPORT_SYMBOL_GPL(mt7615_mac_enable_rtscts);
+
static int
mt7615_mac_wtbl_update_key(struct mt7615_dev *dev, struct mt76_wcid *wcid,
struct ieee80211_key_conf *key,
@@ -1653,7 +1666,7 @@ bool mt7615_rx_check(struct mt76_dev *mdev, void *data, int len)
EXPORT_SYMBOL_GPL(mt7615_rx_check);
void mt7615_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
- struct sk_buff *skb)
+ struct sk_buff *skb, u32 *info)
{
struct mt7615_dev *dev = container_of(mdev, struct mt7615_dev, mt76);
__le32 *rxd = (__le32 *)skb->data;
@@ -1999,7 +2012,7 @@ mt7615_mac_update_mib_stats(struct mt7615_phy *phy)
struct mt7615_dev *dev = phy->dev;
struct mib_stats *mib = &phy->mib;
bool ext_phy = phy != &dev->phy;
- int i, aggr;
+ int i, aggr = 0;
u32 val, val2;
mib->fcs_err_cnt += mt76_get_field(dev, MT_MIB_SDR3(ext_phy),
@@ -2013,7 +2026,6 @@ mt7615_mac_update_mib_stats(struct mt7615_phy *phy)
mib->aggr_per = 1000 * (val - val2) / val;
}
- aggr = ext_phy ? ARRAY_SIZE(dev->mt76.aggr_stats) / 2 : 0;
for (i = 0; i < 4; i++) {
val = mt76_rr(dev, MT_MIB_MB_SDR1(ext_phy, i));
mib->ba_miss_cnt += FIELD_GET(MT_MIB_BA_MISS_COUNT_MASK, val);
@@ -2026,8 +2038,8 @@ mt7615_mac_update_mib_stats(struct mt7615_phy *phy)
val);
val = mt76_rr(dev, MT_TX_AGG_CNT(ext_phy, i));
- dev->mt76.aggr_stats[aggr++] += val & 0xffff;
- dev->mt76.aggr_stats[aggr++] += val >> 16;
+ phy->mt76->aggr_stats[aggr++] += val & 0xffff;
+ phy->mt76->aggr_stats[aggr++] += val >> 16;
}
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/main.c b/drivers/net/wireless/mediatek/mt76/mt7615/main.c
index 8d4733f87cda..ab4c1b4478aa 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/main.c
@@ -83,7 +83,7 @@ static int mt7615_start(struct ieee80211_hw *hw)
ieee80211_queue_delayed_work(hw, &phy->mt76->mac_work, timeout);
if (!running)
- mt7615_mac_reset_counters(dev);
+ mt7615_mac_reset_counters(phy);
out:
mt7615_mutex_release(dev);
@@ -320,7 +320,7 @@ int mt7615_set_channel(struct mt7615_phy *phy)
if (ret)
goto out;
- mt7615_mac_reset_counters(dev);
+ mt7615_mac_reset_counters(phy);
phy->noise = 0;
phy->chfreq = mt76_rr(dev, MT_CHFREQ(ext_phy));
@@ -572,6 +572,9 @@ static void mt7615_bss_info_changed(struct ieee80211_hw *hw,
}
}
+ if (changed & BSS_CHANGED_ERP_CTS_PROT)
+ mt7615_mac_enable_rtscts(dev, vif, info->use_cts_prot);
+
if (changed & BSS_CHANGED_BEACON_ENABLED && info->enable_beacon) {
mt7615_mcu_add_bss_info(phy, vif, NULL, true);
mt7615_mcu_sta_add(phy, vif, NULL, true);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c
index 3dac76e6df4d..83f30305414d 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c
@@ -1119,7 +1119,7 @@ mt7615_mcu_uni_add_bss(struct mt7615_phy *phy, struct ieee80211_vif *vif,
struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv;
return mt76_connac_mcu_uni_add_bss(phy->mt76, vif, &mvif->sta.wcid,
- enable);
+ enable, NULL);
}
static inline int
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h b/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h
index 060d52c81d9e..087d4886162e 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h
@@ -469,10 +469,12 @@ void mt7615_init_work(struct mt7615_dev *dev);
int mt7615_mcu_restart(struct mt76_dev *dev);
void mt7615_update_channel(struct mt76_phy *mphy);
bool mt7615_mac_wtbl_update(struct mt7615_dev *dev, int idx, u32 mask);
-void mt7615_mac_reset_counters(struct mt7615_dev *dev);
+void mt7615_mac_reset_counters(struct mt7615_phy *phy);
void mt7615_mac_cca_stats_reset(struct mt7615_phy *phy);
void mt7615_mac_set_scs(struct mt7615_phy *phy, bool enable);
void mt7615_mac_enable_nf(struct mt7615_dev *dev, bool ext_phy);
+void mt7615_mac_enable_rtscts(struct mt7615_dev *dev,
+ struct ieee80211_vif *vif, bool enable);
void mt7615_mac_sta_poll(struct mt7615_dev *dev);
int mt7615_mac_write_txwi(struct mt7615_dev *dev, __le32 *txwi,
struct sk_buff *skb, struct mt76_wcid *wcid,
@@ -511,7 +513,7 @@ void mt7615_tx_worker(struct mt76_worker *w);
void mt7615_tx_token_put(struct mt7615_dev *dev);
bool mt7615_rx_check(struct mt76_dev *mdev, void *data, int len);
void mt7615_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
- struct sk_buff *skb);
+ struct sk_buff *skb, u32 *info);
void mt7615_sta_ps(struct mt76_dev *mdev, struct ieee80211_sta *sta, bool ps);
int mt7615_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
struct ieee80211_sta *sta);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/regs.h b/drivers/net/wireless/mediatek/mt76/mt7615/regs.h
index 6712ad9faeaa..fa1b9b26b399 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/regs.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/regs.h
@@ -446,6 +446,8 @@ enum mt7615_reg_base {
#define MT_WTBL_RIUCR3_RATE6 GENMASK(19, 8)
#define MT_WTBL_RIUCR3_RATE7 GENMASK(31, 20)
+#define MT_WTBL_W3_RTS BIT(22)
+
#define MT_WTBL_W5_CHANGE_BW_RATE GENMASK(7, 5)
#define MT_WTBL_W5_SHORT_GI_20 BIT(8)
#define MT_WTBL_W5_SHORT_GI_40 BIT(9)
diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac.h b/drivers/net/wireless/mediatek/mt76/mt76_connac.h
index 635192c878cb..8ba883b03e50 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76_connac.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76_connac.h
@@ -187,6 +187,11 @@ static inline bool is_mt7986(struct mt76_dev *dev)
return mt76_chip(dev) == 0x7986;
}
+static inline bool is_mt7996(struct mt76_dev *dev)
+{
+ return mt76_chip(dev) == 0x7990;
+}
+
static inline bool is_mt7622(struct mt76_dev *dev)
{
if (!IS_ENABLED(CONFIG_MT7622_WMAC))
@@ -261,6 +266,17 @@ mt76_connac_txwi_to_txp(struct mt76_dev *dev, struct mt76_txwi_cache *t)
return (void *)(txwi + MT_TXD_SIZE);
}
+static inline u8 mt76_connac_spe_idx(u8 antenna_mask)
+{
+ static const u8 ant_to_spe[] = {0, 0, 1, 0, 3, 2, 4, 0,
+ 9, 8, 6, 10, 16, 12, 18, 0};
+
+ if (antenna_mask >= sizeof(ant_to_spe))
+ return 0;
+
+ return ant_to_spe[antenna_mask];
+}
+
int mt76_connac_pm_wake(struct mt76_phy *phy, struct mt76_connac_pm *pm);
void mt76_connac_power_save_sched(struct mt76_phy *phy,
struct mt76_connac_pm *pm);
diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c
index 34ac3d81a510..fd60123fb284 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c
@@ -417,9 +417,6 @@ mt76_connac2_mac_write_txwi_80211(struct mt76_dev *dev, __le32 *txwi,
if (ieee80211_is_beacon(fc)) {
txwi[3] &= ~cpu_to_le32(MT_TXD3_SW_POWER_MGMT);
txwi[3] |= cpu_to_le32(MT_TXD3_REM_TX_COUNT);
- if (!is_mt7921(dev))
- txwi[7] |= cpu_to_le32(FIELD_PREP(MT_TXD7_SPE_IDX,
- 0x18));
}
if (info->flags & IEEE80211_TX_CTL_INJECTED) {
@@ -550,6 +547,14 @@ void mt76_connac2_mac_write_txwi(struct mt76_dev *dev, __le32 *txwi,
val |= FIELD_PREP(MT_TXD6_TX_RATE, rate);
txwi[6] |= cpu_to_le32(val);
txwi[3] |= cpu_to_le32(MT_TXD3_BA_DISABLE);
+
+ if (!is_mt7921(dev)) {
+ u8 spe_idx = mt76_connac_spe_idx(mphy->antenna_mask);
+
+ if (!spe_idx)
+ spe_idx = 24 + phy_idx;
+ txwi[7] |= cpu_to_le32(FIELD_PREP(MT_TXD7_SPE_IDX, spe_idx));
+ }
}
}
EXPORT_SYMBOL_GPL(mt76_connac2_mac_write_txwi);
@@ -562,7 +567,7 @@ bool mt76_connac2_mac_fill_txs(struct mt76_dev *dev, struct mt76_wcid *wcid,
struct mt76_phy *mphy;
struct rate_info rate = {};
bool cck = false;
- u32 txrate, txs, mode;
+ u32 txrate, txs, mode, stbc;
txs = le32_to_cpu(txs_data[0]);
@@ -582,6 +587,10 @@ bool mt76_connac2_mac_fill_txs(struct mt76_dev *dev, struct mt76_wcid *wcid,
rate.mcs = FIELD_GET(MT_TX_RATE_IDX, txrate);
rate.nss = FIELD_GET(MT_TX_RATE_NSS, txrate) + 1;
+ stbc = FIELD_GET(MT_TX_RATE_STBC, txrate);
+
+ if (stbc && rate.nss > 1)
+ rate.nss >>= 1;
if (rate.nss - 1 < ARRAY_SIZE(stats->tx_nss))
stats->tx_nss[rate.nss - 1]++;
diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
index 011fc9729b38..5a047e630860 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
@@ -65,7 +65,8 @@ int mt76_connac_mcu_init_download(struct mt76_dev *dev, u32 addr, u32 len,
int cmd;
if ((!is_connac_v1(dev) && addr == MCU_PATCH_ADDRESS) ||
- (is_mt7921(dev) && addr == 0x900000))
+ (is_mt7921(dev) && addr == 0x900000) ||
+ (is_mt7996(dev) && addr == 0x900000))
cmd = MCU_CMD(PATCH_START_REQ);
else
cmd = MCU_CMD(TARGET_ADDRESS_LEN_REQ);
@@ -744,6 +745,39 @@ mt76_connac_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
he->pkt_ext = 2;
}
+static void
+mt76_connac_mcu_sta_he_tlv_v2(struct sk_buff *skb, struct ieee80211_sta *sta)
+{
+ struct ieee80211_sta_he_cap *he_cap = &sta->deflink.he_cap;
+ struct ieee80211_he_cap_elem *elem = &he_cap->he_cap_elem;
+ struct sta_rec_he_v2 *he;
+ struct tlv *tlv;
+
+ tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HE_V2, sizeof(*he));
+
+ he = (struct sta_rec_he_v2 *)tlv;
+ memcpy(he->he_phy_cap, elem->phy_cap_info, sizeof(he->he_phy_cap));
+ memcpy(he->he_mac_cap, elem->mac_cap_info, sizeof(he->he_mac_cap));
+
+ switch (sta->deflink.bandwidth) {
+ case IEEE80211_STA_RX_BW_160:
+ if (elem->phy_cap_info[0] &
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G)
+ he->max_nss_mcs[CMD_HE_MCS_BW8080] =
+ he_cap->he_mcs_nss_supp.rx_mcs_80p80;
+
+ he->max_nss_mcs[CMD_HE_MCS_BW160] =
+ he_cap->he_mcs_nss_supp.rx_mcs_160;
+ fallthrough;
+ default:
+ he->max_nss_mcs[CMD_HE_MCS_BW80] =
+ he_cap->he_mcs_nss_supp.rx_mcs_80;
+ break;
+ }
+
+ he->pkt_ext = IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_16US;
+}
+
static u8
mt76_connac_get_phy_mode_v2(struct mt76_phy *mphy, struct ieee80211_vif *vif,
enum nl80211_band band, struct ieee80211_sta *sta)
@@ -838,6 +872,7 @@ void mt76_connac_mcu_sta_tlv(struct mt76_phy *mphy, struct sk_buff *skb,
/* starec he */
if (sta->deflink.he_cap.has_he) {
mt76_connac_mcu_sta_he_tlv(skb, sta);
+ mt76_connac_mcu_sta_he_tlv_v2(skb, sta);
if (band == NL80211_BAND_6GHZ &&
sta_state == MT76_STA_INFO_STATE_ASSOC) {
struct sta_rec_he_6g_capa *he_6g_capa;
@@ -1184,6 +1219,16 @@ void mt76_connac_mcu_sta_ba_tlv(struct sk_buff *skb,
}
EXPORT_SYMBOL_GPL(mt76_connac_mcu_sta_ba_tlv);
+int mt76_connac_mcu_sta_wed_update(struct mt76_dev *dev, struct sk_buff *skb)
+{
+ if (!mtk_wed_device_active(&dev->mmio.wed))
+ return 0;
+
+ return mtk_wed_device_update_msg(&dev->mmio.wed, WED_WO_STA_REC,
+ skb->data, skb->len);
+}
+EXPORT_SYMBOL_GPL(mt76_connac_mcu_sta_wed_update);
+
int mt76_connac_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif *mvif,
struct ieee80211_ampdu_params *params,
int cmd, bool enable, bool tx)
@@ -1209,6 +1254,10 @@ int mt76_connac_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif *mvif,
mt76_connac_mcu_wtbl_ba_tlv(dev, skb, params, enable, tx, sta_wtbl,
wtbl_hdr);
+ ret = mt76_connac_mcu_sta_wed_update(dev, skb);
+ if (ret)
+ return ret;
+
ret = mt76_mcu_skb_send_msg(dev, skb, cmd, true);
if (ret)
return ret;
@@ -1219,6 +1268,10 @@ int mt76_connac_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif *mvif,
mt76_connac_mcu_sta_ba_tlv(skb, params, enable, tx);
+ ret = mt76_connac_mcu_sta_wed_update(dev, skb);
+ if (ret)
+ return ret;
+
return mt76_mcu_skb_send_msg(dev, skb, cmd, true);
}
EXPORT_SYMBOL_GPL(mt76_connac_mcu_sta_ba);
@@ -1313,13 +1366,10 @@ mt76_connac_mcu_uni_bss_he_tlv(struct mt76_phy *phy, struct ieee80211_vif *vif,
he->max_nss_mcs[CMD_HE_MCS_BW8080] = cap->he_mcs_nss_supp.tx_mcs_80p80;
}
-int mt76_connac_mcu_uni_add_bss(struct mt76_phy *phy,
- struct ieee80211_vif *vif,
- struct mt76_wcid *wcid,
- bool enable)
+int mt76_connac_mcu_uni_set_chctx(struct mt76_phy *phy, struct mt76_vif *mvif,
+ struct ieee80211_chanctx_conf *ctx)
{
- struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
- struct cfg80211_chan_def *chandef = &phy->chandef;
+ struct cfg80211_chan_def *chandef = ctx ? &ctx->def : &phy->chandef;
int freq1 = chandef->center_freq1, freq2 = chandef->center_freq2;
enum nl80211_band band = chandef->chan->band;
struct mt76_dev *mdev = phy->dev;
@@ -1328,34 +1378,6 @@ int mt76_connac_mcu_uni_add_bss(struct mt76_phy *phy,
u8 bss_idx;
u8 pad[3];
} __packed hdr;
- struct mt76_connac_bss_basic_tlv basic;
- struct mt76_connac_bss_qos_tlv qos;
- } basic_req = {
- .hdr = {
- .bss_idx = mvif->idx,
- },
- .basic = {
- .tag = cpu_to_le16(UNI_BSS_INFO_BASIC),
- .len = cpu_to_le16(sizeof(struct mt76_connac_bss_basic_tlv)),
- .bcn_interval = cpu_to_le16(vif->bss_conf.beacon_int),
- .dtim_period = vif->bss_conf.dtim_period,
- .omac_idx = mvif->omac_idx,
- .band_idx = mvif->band_idx,
- .wmm_idx = mvif->wmm_idx,
- .active = true, /* keep bss deactivated */
- .phymode = mt76_connac_get_phy_mode(phy, vif, band, NULL),
- },
- .qos = {
- .tag = cpu_to_le16(UNI_BSS_INFO_QBSS),
- .len = cpu_to_le16(sizeof(struct mt76_connac_bss_qos_tlv)),
- .qos = vif->bss_conf.qos,
- },
- };
- struct {
- struct {
- u8 bss_idx;
- u8 pad[3];
- } __packed hdr;
struct rlm_tlv {
__le16 tag;
__le16 len;
@@ -1388,6 +1410,82 @@ int mt76_connac_mcu_uni_add_bss(struct mt76_phy *phy,
.band = band,
},
};
+
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_40:
+ rlm_req.rlm.bw = CMD_CBW_40MHZ;
+ break;
+ case NL80211_CHAN_WIDTH_80:
+ rlm_req.rlm.bw = CMD_CBW_80MHZ;
+ break;
+ case NL80211_CHAN_WIDTH_80P80:
+ rlm_req.rlm.bw = CMD_CBW_8080MHZ;
+ break;
+ case NL80211_CHAN_WIDTH_160:
+ rlm_req.rlm.bw = CMD_CBW_160MHZ;
+ break;
+ case NL80211_CHAN_WIDTH_5:
+ rlm_req.rlm.bw = CMD_CBW_5MHZ;
+ break;
+ case NL80211_CHAN_WIDTH_10:
+ rlm_req.rlm.bw = CMD_CBW_10MHZ;
+ break;
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ case NL80211_CHAN_WIDTH_20:
+ default:
+ rlm_req.rlm.bw = CMD_CBW_20MHZ;
+ rlm_req.rlm.ht_op_info = 0;
+ break;
+ }
+
+ if (rlm_req.rlm.control_channel < rlm_req.rlm.center_chan)
+ rlm_req.rlm.sco = 1; /* SCA */
+ else if (rlm_req.rlm.control_channel > rlm_req.rlm.center_chan)
+ rlm_req.rlm.sco = 3; /* SCB */
+
+ return mt76_mcu_send_msg(mdev, MCU_UNI_CMD(BSS_INFO_UPDATE), &rlm_req,
+ sizeof(rlm_req), true);
+}
+EXPORT_SYMBOL_GPL(mt76_connac_mcu_uni_set_chctx);
+
+int mt76_connac_mcu_uni_add_bss(struct mt76_phy *phy,
+ struct ieee80211_vif *vif,
+ struct mt76_wcid *wcid,
+ bool enable,
+ struct ieee80211_chanctx_conf *ctx)
+{
+ struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
+ struct cfg80211_chan_def *chandef = ctx ? &ctx->def : &phy->chandef;
+ enum nl80211_band band = chandef->chan->band;
+ struct mt76_dev *mdev = phy->dev;
+ struct {
+ struct {
+ u8 bss_idx;
+ u8 pad[3];
+ } __packed hdr;
+ struct mt76_connac_bss_basic_tlv basic;
+ struct mt76_connac_bss_qos_tlv qos;
+ } basic_req = {
+ .hdr = {
+ .bss_idx = mvif->idx,
+ },
+ .basic = {
+ .tag = cpu_to_le16(UNI_BSS_INFO_BASIC),
+ .len = cpu_to_le16(sizeof(struct mt76_connac_bss_basic_tlv)),
+ .bcn_interval = cpu_to_le16(vif->bss_conf.beacon_int),
+ .dtim_period = vif->bss_conf.dtim_period,
+ .omac_idx = mvif->omac_idx,
+ .band_idx = mvif->band_idx,
+ .wmm_idx = mvif->wmm_idx,
+ .active = true, /* keep bss deactivated */
+ .phymode = mt76_connac_get_phy_mode(phy, vif, band, NULL),
+ },
+ .qos = {
+ .tag = cpu_to_le16(UNI_BSS_INFO_QBSS),
+ .len = cpu_to_le16(sizeof(struct mt76_connac_bss_qos_tlv)),
+ .qos = vif->bss_conf.qos,
+ },
+ };
int err, conn_type;
u8 idx, basic_phy;
@@ -1474,40 +1572,7 @@ int mt76_connac_mcu_uni_add_bss(struct mt76_phy *phy,
return err;
}
- switch (chandef->width) {
- case NL80211_CHAN_WIDTH_40:
- rlm_req.rlm.bw = CMD_CBW_40MHZ;
- break;
- case NL80211_CHAN_WIDTH_80:
- rlm_req.rlm.bw = CMD_CBW_80MHZ;
- break;
- case NL80211_CHAN_WIDTH_80P80:
- rlm_req.rlm.bw = CMD_CBW_8080MHZ;
- break;
- case NL80211_CHAN_WIDTH_160:
- rlm_req.rlm.bw = CMD_CBW_160MHZ;
- break;
- case NL80211_CHAN_WIDTH_5:
- rlm_req.rlm.bw = CMD_CBW_5MHZ;
- break;
- case NL80211_CHAN_WIDTH_10:
- rlm_req.rlm.bw = CMD_CBW_10MHZ;
- break;
- case NL80211_CHAN_WIDTH_20_NOHT:
- case NL80211_CHAN_WIDTH_20:
- default:
- rlm_req.rlm.bw = CMD_CBW_20MHZ;
- rlm_req.rlm.ht_op_info = 0;
- break;
- }
-
- if (rlm_req.rlm.control_channel < rlm_req.rlm.center_chan)
- rlm_req.rlm.sco = 1; /* SCA */
- else if (rlm_req.rlm.control_channel > rlm_req.rlm.center_chan)
- rlm_req.rlm.sco = 3; /* SCB */
-
- return mt76_mcu_send_msg(mdev, MCU_UNI_CMD(BSS_INFO_UPDATE), &rlm_req,
- sizeof(rlm_req), true);
+ return mt76_connac_mcu_uni_set_chctx(phy, mvif, ctx);
}
EXPORT_SYMBOL_GPL(mt76_connac_mcu_uni_add_bss);
@@ -1525,6 +1590,9 @@ int mt76_connac_mcu_hw_scan(struct mt76_phy *phy, struct ieee80211_vif *vif,
struct mt76_connac_hw_scan_req *req;
struct sk_buff *skb;
+ if (test_bit(MT76_HW_SCANNING, &phy->state))
+ return -EBUSY;
+
skb = mt76_mcu_msg_alloc(mdev, NULL, sizeof(*req));
if (!skb)
return -ENOMEM;
@@ -2646,6 +2714,10 @@ int mt76_connac_mcu_add_key(struct mt76_dev *dev, struct ieee80211_vif *vif,
if (ret)
return ret;
+ ret = mt76_connac_mcu_sta_wed_update(dev, skb);
+ if (ret)
+ return ret;
+
return mt76_mcu_skb_send_msg(dev, skb, mcu_cmd, true);
}
EXPORT_SYMBOL_GPL(mt76_connac_mcu_add_key);
@@ -2834,6 +2906,9 @@ mt76_connac_mcu_send_ram_firmware(struct mt76_dev *dev,
len = le32_to_cpu(region->len);
addr = le32_to_cpu(region->addr);
+ if (region->feature_set & FW_FEATURE_NON_DL)
+ goto next;
+
if (region->feature_set & FW_FEATURE_OVERRIDE_ADDR)
override = addr;
@@ -2850,6 +2925,7 @@ mt76_connac_mcu_send_ram_firmware(struct mt76_dev *dev,
return err;
}
+next:
offset += len;
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
index 718f427d8f6b..f1e942b9a887 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
@@ -63,7 +63,7 @@ struct mt76_connac2_mcu_txd {
} __packed __aligned(4);
/**
- * struct mt76_connac2_mcu_uni_txd - mcu command descriptor for firmware v3
+ * struct mt76_connac2_mcu_uni_txd - mcu command descriptor for connac2 and connac3
* @txd: hardware descriptor
* @len: total length not including txd
* @cid: command identifier
@@ -121,11 +121,13 @@ struct mt76_connac2_mcu_rxd {
u8 eid;
u8 seq;
- u8 rsv[2];
-
+ u8 option;
+ u8 rsv;
u8 ext_eid;
u8 rsv1[2];
u8 s2d_index;
+
+ u8 tlv[0];
};
struct mt76_connac2_patch_hdr {
@@ -354,6 +356,16 @@ struct sta_rec_he {
u8 rsv2[2];
} __packed;
+struct sta_rec_he_v2 {
+ __le16 tag;
+ __le16 len;
+ u8 he_mac_cap[6];
+ u8 he_phy_cap[11];
+ u8 pkt_ext;
+ /* 0: BW80, 1: BW160, 2: BW8080 */
+ __le16 max_nss_mcs[CMD_HE_MCS_BW_NUM];
+} __packed;
+
struct sta_rec_amsdu {
__le16 tag;
__le16 len;
@@ -391,7 +403,8 @@ struct sta_rec_phy {
u8 ampdu;
u8 rts_policy;
u8 rcpi;
- u8 rsv[2];
+ u8 max_ampdu_len; /* connac3 */
+ u8 rsv[1];
} __packed;
struct sta_rec_he_6g_capa {
@@ -452,8 +465,8 @@ struct sta_rec_bf {
u8 ibf_dbw;
u8 ibf_ncol;
u8 ibf_nrow;
- u8 nrow_bw160;
- u8 ncol_bw160;
+ u8 nrow_gt_bw80;
+ u8 ncol_gt_bw80;
u8 ru_start_idx;
u8 ru_end_idx;
@@ -580,7 +593,7 @@ struct sta_rec_ra_fixed {
struct sta_phy phy;
- u8 spe_en;
+ u8 spe_idx;
u8 short_preamble;
u8 is_5g;
u8 mmps_mode;
@@ -779,6 +792,9 @@ enum {
STA_REC_BFEE,
STA_REC_PHY = 0x15,
STA_REC_HE_6G = 0x17,
+ STA_REC_HE_V2 = 0x19,
+ STA_REC_HDRT = 0x28,
+ STA_REC_HDR_TRANS = 0x2B,
STA_REC_MAX_NUM
};
@@ -946,6 +962,9 @@ enum {
DEV_INFO_MAX_NUM
};
+#define MCU_UNI_CMD_EVENT BIT(1)
+#define MCU_UNI_CMD_UNSOLICITED_EVENT BIT(2)
+
/* event table */
enum {
MCU_EVENT_TARGET_ADDRESS_LEN = 0x01,
@@ -981,6 +1000,17 @@ enum {
MCU_EXT_EVENT_MURU_CTRL = 0x9f,
};
+/* unified event table */
+enum {
+ MCU_UNI_EVENT_RESULT = 0x01,
+ MCU_UNI_EVENT_FW_LOG_2_HOST = 0x04,
+ MCU_UNI_EVENT_IE_COUNTDOWN = 0x09,
+ MCU_UNI_EVENT_RDD_REPORT = 0x11,
+};
+
+#define MCU_UNI_CMD_EVENT BIT(1)
+#define MCU_UNI_CMD_UNSOLICITED_EVENT BIT(2)
+
enum {
MCU_Q_QUERY,
MCU_Q_SET,
@@ -1063,10 +1093,11 @@ enum {
#define MCU_CMD_ACK BIT(0)
#define MCU_CMD_UNI BIT(1)
-#define MCU_CMD_QUERY BIT(2)
+#define MCU_CMD_SET BIT(2)
#define MCU_CMD_UNI_EXT_ACK (MCU_CMD_ACK | MCU_CMD_UNI | \
- MCU_CMD_QUERY)
+ MCU_CMD_SET)
+#define MCU_CMD_UNI_QUERY_ACK (MCU_CMD_ACK | MCU_CMD_UNI)
#define __MCU_CMD_FIELD_ID GENMASK(7, 0)
#define __MCU_CMD_FIELD_EXT_ID GENMASK(15, 8)
@@ -1074,6 +1105,7 @@ enum {
#define __MCU_CMD_FIELD_UNI BIT(17)
#define __MCU_CMD_FIELD_CE BIT(18)
#define __MCU_CMD_FIELD_WA BIT(19)
+#define __MCU_CMD_FIELD_WM BIT(20)
#define MCU_CMD(_t) FIELD_PREP(__MCU_CMD_FIELD_ID, \
MCU_CMD_##_t)
@@ -1095,6 +1127,16 @@ enum {
FIELD_PREP(__MCU_CMD_FIELD_EXT_ID, \
MCU_WA_PARAM_CMD_##_t))
+#define MCU_WM_UNI_CMD(_t) (MCU_UNI_CMD(_t) | \
+ __MCU_CMD_FIELD_WM)
+#define MCU_WM_UNI_CMD_QUERY(_t) (MCU_UNI_CMD(_t) | \
+ __MCU_CMD_FIELD_QUERY | \
+ __MCU_CMD_FIELD_WM)
+#define MCU_WA_UNI_CMD(_t) (MCU_UNI_CMD(_t) | \
+ __MCU_CMD_FIELD_WA)
+#define MCU_WMWA_UNI_CMD(_t) (MCU_WM_UNI_CMD(_t) | \
+ __MCU_CMD_FIELD_WA)
+
enum {
MCU_EXT_CMD_EFUSE_ACCESS = 0x01,
MCU_EXT_CMD_RF_REG_ACCESS = 0x02,
@@ -1148,10 +1190,33 @@ enum {
MCU_UNI_CMD_DEV_INFO_UPDATE = 0x01,
MCU_UNI_CMD_BSS_INFO_UPDATE = 0x02,
MCU_UNI_CMD_STA_REC_UPDATE = 0x03,
+ MCU_UNI_CMD_EDCA_UPDATE = 0x04,
MCU_UNI_CMD_SUSPEND = 0x05,
MCU_UNI_CMD_OFFLOAD = 0x06,
MCU_UNI_CMD_HIF_CTRL = 0x07,
+ MCU_UNI_CMD_BAND_CONFIG = 0x08,
+ MCU_UNI_CMD_REPT_MUAR = 0x09,
+ MCU_UNI_CMD_WSYS_CONFIG = 0x0b,
+ MCU_UNI_CMD_REG_ACCESS = 0x0d,
+ MCU_UNI_CMD_POWER_CREL = 0x0f,
+ MCU_UNI_CMD_RX_HDR_TRANS = 0x12,
+ MCU_UNI_CMD_SER = 0x13,
+ MCU_UNI_CMD_TWT = 0x14,
+ MCU_UNI_CMD_RDD_CTRL = 0x19,
+ MCU_UNI_CMD_GET_MIB_INFO = 0x22,
MCU_UNI_CMD_SNIFFER = 0x24,
+ MCU_UNI_CMD_SR = 0x25,
+ MCU_UNI_CMD_ROC = 0x27,
+ MCU_UNI_CMD_TXPOWER = 0x2b,
+ MCU_UNI_CMD_EFUSE_CTRL = 0x2d,
+ MCU_UNI_CMD_RA = 0x2f,
+ MCU_UNI_CMD_MURU = 0x31,
+ MCU_UNI_CMD_BF = 0x33,
+ MCU_UNI_CMD_CHANNEL_SWITCH = 0x34,
+ MCU_UNI_CMD_THERMAL = 0x35,
+ MCU_UNI_CMD_VOW = 0x37,
+ MCU_UNI_CMD_RRO = 0x57,
+ MCU_UNI_CMD_OFFCH_SCAN_CTRL = 0x58,
};
enum {
@@ -1201,14 +1266,23 @@ enum {
enum {
UNI_BSS_INFO_BASIC = 0,
+ UNI_BSS_INFO_RA = 1,
UNI_BSS_INFO_RLM = 2,
UNI_BSS_INFO_BSS_COLOR = 4,
UNI_BSS_INFO_HE_BASIC = 5,
UNI_BSS_INFO_BCN_CONTENT = 7,
+ UNI_BSS_INFO_BCN_CSA = 8,
+ UNI_BSS_INFO_BCN_BCC = 9,
+ UNI_BSS_INFO_BCN_MBSSID = 10,
+ UNI_BSS_INFO_RATE = 11,
UNI_BSS_INFO_QBSS = 15,
+ UNI_BSS_INFO_SEC = 16,
+ UNI_BSS_INFO_TXCMD = 18,
UNI_BSS_INFO_UAPSD = 19,
UNI_BSS_INFO_PS = 21,
UNI_BSS_INFO_BCNFT = 22,
+ UNI_BSS_INFO_OFFLOAD = 25,
+ UNI_BSS_INFO_MLD = 26,
};
enum {
@@ -1736,10 +1810,14 @@ int mt76_connac_mcu_uni_add_dev(struct mt76_phy *phy,
int mt76_connac_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif *mvif,
struct ieee80211_ampdu_params *params,
int cmd, bool enable, bool tx);
+int mt76_connac_mcu_uni_set_chctx(struct mt76_phy *phy,
+ struct mt76_vif *vif,
+ struct ieee80211_chanctx_conf *ctx);
int mt76_connac_mcu_uni_add_bss(struct mt76_phy *phy,
struct ieee80211_vif *vif,
struct mt76_wcid *wcid,
- bool enable);
+ bool enable,
+ struct ieee80211_chanctx_conf *ctx);
int mt76_connac_mcu_sta_cmd(struct mt76_phy *phy,
struct mt76_sta_cmd_info *info);
void mt76_connac_mcu_beacon_loss_iter(void *priv, u8 *mac,
@@ -1813,6 +1891,7 @@ int mt76_connac_mcu_set_pm(struct mt76_dev *dev, int band, int enter);
int mt76_connac_mcu_restart(struct mt76_dev *dev);
int mt76_connac_mcu_rdd_cmd(struct mt76_dev *dev, int cmd, u8 index,
u8 rx_sel, u8 val);
+int mt76_connac_mcu_sta_wed_update(struct mt76_dev *dev, struct sk_buff *skb);
int mt76_connac2_load_ram(struct mt76_dev *dev, const char *fw_wm,
const char *fw_wa);
int mt76_connac2_load_patch(struct mt76_dev *dev, const char *fw_name);
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.c
index da2ca2563ac9..c3a392a1a659 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.c
@@ -151,7 +151,7 @@ static s8 mt76x0_get_delta(struct mt76x02_dev *dev)
void mt76x0_get_tx_power_per_rate(struct mt76x02_dev *dev,
struct ieee80211_channel *chan,
- struct mt76_rate_power *t)
+ struct mt76x02_rate_power *t)
{
bool is_2ghz = chan->band == NL80211_BAND_2GHZ;
u16 val, addr;
@@ -179,31 +179,19 @@ void mt76x0_get_tx_power_per_rate(struct mt76x02_dev *dev,
/* ht-vht mcs 1ss 0, 1, 2, 3 */
addr = is_2ghz ? MT_EE_TX_POWER_BYRATE_BASE + 6 : 0x124;
val = mt76x02_eeprom_get(dev, addr);
- t->ht[0] = t->ht[1] = t->vht[0] = t->vht[1] = s6_to_s8(val);
- t->ht[2] = t->ht[3] = t->vht[2] = t->vht[3] = s6_to_s8(val >> 8);
+ t->ht[0] = t->ht[1] = s6_to_s8(val);
+ t->ht[2] = t->ht[3] = s6_to_s8(val >> 8);
/* ht-vht mcs 1ss 4, 5, 6 */
addr = is_2ghz ? MT_EE_TX_POWER_BYRATE_BASE + 8 : 0x126;
val = mt76x02_eeprom_get(dev, addr);
- t->ht[4] = t->ht[5] = t->vht[4] = t->vht[5] = s6_to_s8(val);
- t->ht[6] = t->ht[7] = t->vht[6] = t->vht[7] = s6_to_s8(val >> 8);
-
- /* ht-vht mcs 1ss 0, 1, 2, 3 stbc */
- addr = is_2ghz ? MT_EE_TX_POWER_BYRATE_BASE + 14 : 0xec;
- val = mt76x02_eeprom_get(dev, addr);
- t->stbc[0] = t->stbc[1] = s6_to_s8(val);
- t->stbc[2] = t->stbc[3] = s6_to_s8(val >> 8);
-
- /* ht-vht mcs 1ss 4, 5, 6 stbc */
- addr = is_2ghz ? MT_EE_TX_POWER_BYRATE_BASE + 16 : 0xee;
- val = mt76x02_eeprom_get(dev, addr);
- t->stbc[4] = t->stbc[5] = s6_to_s8(val);
- t->stbc[6] = t->stbc[7] = s6_to_s8(val >> 8);
+ t->ht[4] = t->ht[5] = s6_to_s8(val);
+ t->ht[6] = t->ht[7] = s6_to_s8(val >> 8);
/* vht mcs 8, 9 5GHz */
val = mt76x02_eeprom_get(dev, 0x12c);
- t->vht[8] = s6_to_s8(val);
- t->vht[9] = s6_to_s8(val >> 8);
+ t->vht[0] = s6_to_s8(val);
+ t->vht[1] = s6_to_s8(val >> 8);
delta = mt76x0_tssi_enabled(dev) ? 0 : mt76x0_get_delta(dev);
mt76x02_add_rate_power_offset(t, delta);
@@ -235,7 +223,7 @@ void mt76x0_get_power_info(struct mt76x02_dev *dev,
data = mt76x02_eeprom_get(dev, MT_EE_5G_TARGET_POWER);
else
data = mt76x02_eeprom_get(dev, MT_EE_2G_TARGET_POWER);
- target_power = (data & 0xff) - dev->mt76.rate_power.ofdm[7];
+ target_power = (data & 0xff) - dev->rate_power.ofdm[7];
*tp = target_power + mt76x0_get_delta(dev);
return;
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.h b/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.h
index 15540ce8db87..08f1b10bf3ba 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.h
@@ -19,7 +19,7 @@ int mt76x0_eeprom_init(struct mt76x02_dev *dev);
void mt76x0_read_rx_gain(struct mt76x02_dev *dev);
void mt76x0_get_tx_power_per_rate(struct mt76x02_dev *dev,
struct ieee80211_channel *chan,
- struct mt76_rate_power *t);
+ struct mt76x02_rate_power *t);
void mt76x0_get_power_info(struct mt76x02_dev *dev,
struct ieee80211_channel *chan, s8 *tp);
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/init.c b/drivers/net/wireless/mediatek/mt76/mt76x0/init.c
index 66d47c70111a..6257460f8de5 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x0/init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x0/init.c
@@ -217,7 +217,7 @@ mt76x0_init_txpower(struct mt76x02_dev *dev,
struct ieee80211_supported_band *sband)
{
struct ieee80211_channel *chan;
- struct mt76_rate_power t;
+ struct mt76x02_rate_power t;
s8 tp;
int i;
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/phy.c b/drivers/net/wireless/mediatek/mt76/mt76x0/phy.c
index e91c314cdfac..6c6c8ada7943 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x0/phy.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x0/phy.c
@@ -595,10 +595,7 @@ mt76x0_phy_get_target_power(struct mt76x02_dev *dev, u8 tx_mode,
case 0:
/* cck rates */
tx_rate = (info[0] & 0x60) >> 5;
- if (tx_rate > 3)
- return -EINVAL;
-
- *target_power = cur_power + dev->mt76.rate_power.cck[tx_rate];
+ *target_power = cur_power + dev->rate_power.cck[tx_rate];
*target_pa_power = mt76x0_phy_get_rf_pa_mode(dev, 0, tx_rate);
break;
case 1: {
@@ -635,7 +632,7 @@ mt76x0_phy_get_target_power(struct mt76x02_dev *dev, u8 tx_mode,
return -EINVAL;
}
- *target_power = cur_power + dev->mt76.rate_power.ofdm[index];
+ *target_power = cur_power + dev->rate_power.ofdm[index];
*target_pa_power = mt76x0_phy_get_rf_pa_mode(dev, 0, index + 4);
break;
}
@@ -645,7 +642,7 @@ mt76x0_phy_get_target_power(struct mt76x02_dev *dev, u8 tx_mode,
if (tx_rate > 9)
return -EINVAL;
- *target_power = cur_power + dev->mt76.rate_power.vht[tx_rate];
+ *target_power = cur_power + dev->rate_power.vht[tx_rate];
*target_pa_power = mt76x0_phy_get_rf_pa_mode(dev, 1, tx_rate);
break;
default:
@@ -654,7 +651,7 @@ mt76x0_phy_get_target_power(struct mt76x02_dev *dev, u8 tx_mode,
if (tx_rate > 9)
return -EINVAL;
- *target_power = cur_power + dev->mt76.rate_power.ht[tx_rate];
+ *target_power = cur_power + dev->rate_power.ht[tx_rate];
*target_pa_power = mt76x0_phy_get_rf_pa_mode(dev, 1, tx_rate);
break;
}
@@ -841,7 +838,7 @@ static void mt76x0_phy_tssi_calibrate(struct mt76x02_dev *dev)
void mt76x0_phy_set_txpower(struct mt76x02_dev *dev)
{
- struct mt76_rate_power *t = &dev->mt76.rate_power;
+ struct mt76x02_rate_power *t = &dev->rate_power;
s8 info;
mt76x0_get_tx_power_per_rate(dev, dev->mphy.chandef.chan, t);
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02.h b/drivers/net/wireless/mediatek/mt76/mt76x02.h
index 50eaeff11af3..4cd63bacd742 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02.h
@@ -72,6 +72,18 @@ struct mt76x02_beacon_ops {
#define mt76x02_pre_tbtt_enable(dev, enable) \
(dev)->beacon_ops->pre_tbtt_enable(dev, enable)
+struct mt76x02_rate_power {
+ union {
+ struct {
+ s8 cck[4];
+ s8 ofdm[8];
+ s8 ht[16];
+ s8 vht[2];
+ };
+ s8 all[30];
+ };
+};
+
struct mt76x02_dev {
union { /* must be first */
struct mt76_dev mt76;
@@ -107,6 +119,8 @@ struct mt76x02_dev {
u8 beacon_hang_check;
u8 mcu_timeout;
+ struct mt76x02_rate_power rate_power;
+
struct mt76x02_calibration cal;
int txpower_conf;
@@ -174,7 +188,7 @@ int mt76x02_set_rts_threshold(struct ieee80211_hw *hw, u32 val);
void mt76x02_remove_hdr_pad(struct sk_buff *skb, int len);
bool mt76x02_tx_status_data(struct mt76_dev *mdev, u8 *update);
void mt76x02_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
- struct sk_buff *skb);
+ struct sk_buff *skb, u32 *info);
void mt76x02_rx_poll_complete(struct mt76_dev *mdev, enum mt76_rxq_id q);
irqreturn_t mt76x02_irq_handler(int irq, void *dev_instance);
void mt76x02_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control,
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_debugfs.c b/drivers/net/wireless/mediatek/mt76/mt76x02_debugfs.c
index c4fe1c436aaa..8ce4bf44733d 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_debugfs.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_debugfs.c
@@ -20,7 +20,7 @@ mt76x02_ampdu_stat_show(struct seq_file *file, void *data)
seq_puts(file, "Count: ");
for (j = 0; j < 8; j++)
seq_printf(file, "%8d | ",
- dev->mt76.aggr_stats[i * 8 + j]);
+ dev->mphy.aggr_stats[i * 8 + j]);
seq_puts(file, "\n");
seq_puts(file, "--------");
for (j = 0; j < 8; j++)
@@ -114,6 +114,21 @@ mt76_edcca_get(void *data, u64 *val)
DEFINE_DEBUGFS_ATTRIBUTE(fops_edcca, mt76_edcca_get, mt76_edcca_set,
"%lld\n");
+static int mt76x02_read_rate_txpower(struct seq_file *s, void *data)
+{
+ struct mt76x02_dev *dev = dev_get_drvdata(s->private);
+
+ mt76_seq_puts_array(s, "CCK", dev->rate_power.cck,
+ ARRAY_SIZE(dev->rate_power.cck));
+ mt76_seq_puts_array(s, "OFDM", dev->rate_power.ofdm,
+ ARRAY_SIZE(dev->rate_power.ofdm));
+ mt76_seq_puts_array(s, "HT", dev->rate_power.ht,
+ ARRAY_SIZE(dev->rate_power.ht));
+ mt76_seq_puts_array(s, "VHT", dev->rate_power.vht,
+ ARRAY_SIZE(dev->rate_power.vht));
+ return 0;
+}
+
void mt76x02_init_debugfs(struct mt76x02_dev *dev)
{
struct dentry *dir;
@@ -133,6 +148,8 @@ void mt76x02_init_debugfs(struct mt76x02_dev *dev)
debugfs_create_devm_seqfile(dev->mt76.dev, "txpower", dir,
read_txpower);
+ debugfs_create_devm_seqfile(dev->mt76.dev, "rate_txpower", dir,
+ mt76x02_read_rate_txpower);
debugfs_create_devm_seqfile(dev->mt76.dev, "agc", dir, read_agc);
debugfs_create_u32("tx_hang_reset", 0400, dir, &dev->tx_hang_reset);
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_eeprom.h b/drivers/net/wireless/mediatek/mt76/mt76x02_eeprom.h
index 99941a4700f3..13fa70853b0d 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_eeprom.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_eeprom.h
@@ -62,8 +62,6 @@ enum mt76x02_eeprom_field {
MT_EE_TX_POWER_HT_MCS4 = 0x0a8,
MT_EE_TX_POWER_HT_MCS8 = 0x0aa,
MT_EE_TX_POWER_HT_MCS12 = 0x0ac,
- MT_EE_TX_POWER_VHT_MCS0 = 0x0ba,
- MT_EE_TX_POWER_VHT_MCS4 = 0x0bc,
MT_EE_TX_POWER_VHT_MCS8 = 0x0be,
MT_EE_2G_TARGET_POWER = 0x0d0,
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c
index 93d96739f802..d3f74473e6fb 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c
@@ -25,7 +25,7 @@ void mt76x02_mac_reset_counters(struct mt76x02_dev *dev)
for (i = 0; i < 16; i++)
mt76_rr(dev, MT_TX_STAT_FIFO);
- memset(dev->mt76.aggr_stats, 0, sizeof(dev->mt76.aggr_stats));
+ memset(dev->mphy.aggr_stats, 0, sizeof(dev->mphy.aggr_stats));
}
EXPORT_SYMBOL_GPL(mt76x02_mac_reset_counters);
@@ -1191,8 +1191,8 @@ void mt76x02_mac_work(struct work_struct *work)
for (i = 0, idx = 0; i < 16; i++) {
u32 val = mt76_rr(dev, MT_TX_AGG_CNT(i));
- dev->mt76.aggr_stats[idx++] += val & 0xffff;
- dev->mt76.aggr_stats[idx++] += val >> 16;
+ dev->mphy.aggr_stats[idx++] += val & 0xffff;
+ dev->mphy.aggr_stats[idx++] += val >> 16;
}
mt76x02_check_mac_err(dev);
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_phy.c b/drivers/net/wireless/mediatek/mt76/mt76x02_phy.c
index 2e53b0c1afdd..cbe7e6f0c29a 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_phy.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_phy.c
@@ -59,7 +59,7 @@ mt76x02_tx_power_mask(u8 v1, u8 v2, u8 v3, u8 v4)
return val;
}
-int mt76x02_get_max_rate_power(struct mt76_rate_power *r)
+int mt76x02_get_max_rate_power(struct mt76x02_rate_power *r)
{
s8 ret = 0;
int i;
@@ -71,7 +71,7 @@ int mt76x02_get_max_rate_power(struct mt76_rate_power *r)
}
EXPORT_SYMBOL_GPL(mt76x02_get_max_rate_power);
-void mt76x02_limit_rate_power(struct mt76_rate_power *r, int limit)
+void mt76x02_limit_rate_power(struct mt76x02_rate_power *r, int limit)
{
int i;
@@ -81,7 +81,7 @@ void mt76x02_limit_rate_power(struct mt76_rate_power *r, int limit)
}
EXPORT_SYMBOL_GPL(mt76x02_limit_rate_power);
-void mt76x02_add_rate_power_offset(struct mt76_rate_power *r, int offset)
+void mt76x02_add_rate_power_offset(struct mt76x02_rate_power *r, int offset)
{
int i;
@@ -92,7 +92,7 @@ EXPORT_SYMBOL_GPL(mt76x02_add_rate_power_offset);
void mt76x02_phy_set_txpower(struct mt76x02_dev *dev, int txp_0, int txp_1)
{
- struct mt76_rate_power *t = &dev->mt76.rate_power;
+ struct mt76x02_rate_power *t = &dev->rate_power;
mt76_rmw_field(dev, MT_TX_ALC_CFG_0, MT_TX_ALC_CFG_0_CH_INIT_0, txp_0);
mt76_rmw_field(dev, MT_TX_ALC_CFG_0, MT_TX_ALC_CFG_0_CH_INIT_1, txp_1);
@@ -107,17 +107,17 @@ void mt76x02_phy_set_txpower(struct mt76x02_dev *dev, int txp_0, int txp_1)
mt76x02_tx_power_mask(t->ht[4], t->ht[6], t->ht[8],
t->ht[10]));
mt76_wr(dev, MT_TX_PWR_CFG_3,
- mt76x02_tx_power_mask(t->ht[12], t->ht[14], t->stbc[0],
- t->stbc[2]));
+ mt76x02_tx_power_mask(t->ht[12], t->ht[14], t->ht[0],
+ t->ht[2]));
mt76_wr(dev, MT_TX_PWR_CFG_4,
- mt76x02_tx_power_mask(t->stbc[4], t->stbc[6], 0, 0));
+ mt76x02_tx_power_mask(t->ht[4], t->ht[6], 0, 0));
mt76_wr(dev, MT_TX_PWR_CFG_7,
- mt76x02_tx_power_mask(t->ofdm[7], t->vht[8], t->ht[7],
- t->vht[9]));
+ mt76x02_tx_power_mask(t->ofdm[7], t->vht[0], t->ht[7],
+ t->vht[1]));
mt76_wr(dev, MT_TX_PWR_CFG_8,
- mt76x02_tx_power_mask(t->ht[14], 0, t->vht[8], t->vht[9]));
+ mt76x02_tx_power_mask(t->ht[14], 0, t->vht[0], t->vht[1]));
mt76_wr(dev, MT_TX_PWR_CFG_9,
- mt76x02_tx_power_mask(t->ht[7], 0, t->stbc[8], t->stbc[9]));
+ mt76x02_tx_power_mask(t->ht[7], 0, t->vht[0], t->vht[1]));
}
EXPORT_SYMBOL_GPL(mt76x02_phy_set_txpower);
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_phy.h b/drivers/net/wireless/mediatek/mt76/mt76x02_phy.h
index 1def25bf735a..84d8a6138b3e 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_phy.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_phy.h
@@ -34,10 +34,10 @@ mt76x02_get_low_rssi_gain_thresh(struct mt76x02_dev *dev)
}
}
-void mt76x02_add_rate_power_offset(struct mt76_rate_power *r, int offset);
+void mt76x02_add_rate_power_offset(struct mt76x02_rate_power *r, int offset);
void mt76x02_phy_set_txpower(struct mt76x02_dev *dev, int txp_0, int txp_2);
-void mt76x02_limit_rate_power(struct mt76_rate_power *r, int limit);
-int mt76x02_get_max_rate_power(struct mt76_rate_power *r);
+void mt76x02_limit_rate_power(struct mt76x02_rate_power *r, int limit);
+int mt76x02_get_max_rate_power(struct mt76x02_rate_power *r);
void mt76x02_phy_set_rxpath(struct mt76x02_dev *dev);
void mt76x02_phy_set_txdac(struct mt76x02_dev *dev);
void mt76x02_phy_set_bw(struct mt76x02_dev *dev, int width, u8 ctrl);
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_txrx.c b/drivers/net/wireless/mediatek/mt76/mt76x02_txrx.c
index 96fdf423a348..d8bc4ae185f5 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_txrx.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_txrx.c
@@ -33,7 +33,7 @@ void mt76x02_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control,
EXPORT_SYMBOL_GPL(mt76x02_tx);
void mt76x02_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
- struct sk_buff *skb)
+ struct sk_buff *skb, u32 *info)
{
struct mt76x02_dev *dev = container_of(mdev, struct mt76x02_dev, mt76);
void *rxwi = skb->data;
@@ -62,23 +62,23 @@ s8 mt76x02_tx_get_max_txpwr_adj(struct mt76x02_dev *dev,
u8 mcs = ieee80211_rate_get_vht_mcs(rate);
if (mcs == 8 || mcs == 9) {
- max_txpwr = dev->mt76.rate_power.vht[8];
+ max_txpwr = dev->rate_power.vht[0];
} else {
u8 nss, idx;
nss = ieee80211_rate_get_vht_nss(rate);
idx = ((nss - 1) << 3) + mcs;
- max_txpwr = dev->mt76.rate_power.ht[idx & 0xf];
+ max_txpwr = dev->rate_power.ht[idx & 0xf];
}
} else if (rate->flags & IEEE80211_TX_RC_MCS) {
- max_txpwr = dev->mt76.rate_power.ht[rate->idx & 0xf];
+ max_txpwr = dev->rate_power.ht[rate->idx & 0xf];
} else {
enum nl80211_band band = dev->mphy.chandef.chan->band;
if (band == NL80211_BAND_2GHZ) {
const struct ieee80211_rate *r;
struct wiphy *wiphy = dev->mt76.hw->wiphy;
- struct mt76_rate_power *rp = &dev->mt76.rate_power;
+ struct mt76x02_rate_power *rp = &dev->rate_power;
r = &wiphy->bands[band]->bitrates[rate->idx];
if (r->flags & IEEE80211_RATE_SHORT_PREAMBLE)
@@ -86,7 +86,7 @@ s8 mt76x02_tx_get_max_txpwr_adj(struct mt76x02_dev *dev,
else
max_txpwr = rp->ofdm[r->hw_value & 0x7];
} else {
- max_txpwr = dev->mt76.rate_power.ofdm[rate->idx & 0x7];
+ max_txpwr = dev->rate_power.ofdm[rate->idx & 0x7];
}
}
@@ -112,7 +112,7 @@ void mt76x02_tx_set_txpwr_auto(struct mt76x02_dev *dev, s8 txpwr)
s8 txpwr_adj;
txpwr_adj = mt76x02_tx_get_txpwr_adj(dev, txpwr,
- dev->mt76.rate_power.ofdm[4]);
+ dev->rate_power.ofdm[4]);
mt76_rmw_field(dev, MT_PROT_AUTO_TX_CFG,
MT_PROT_AUTO_TX_CFG_PROT_PADJ, txpwr_adj);
mt76_rmw_field(dev, MT_PROT_AUTO_TX_CFG,
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.c
index c57e05a5c65e..d5809408d1d3 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.c
@@ -280,7 +280,7 @@ void mt76x2_read_rx_gain(struct mt76x02_dev *dev)
}
EXPORT_SYMBOL_GPL(mt76x2_read_rx_gain);
-void mt76x2_get_rate_power(struct mt76x02_dev *dev, struct mt76_rate_power *t,
+void mt76x2_get_rate_power(struct mt76x02_dev *dev, struct mt76x02_rate_power *t,
struct ieee80211_channel *chan)
{
bool is_5ghz;
@@ -324,22 +324,10 @@ void mt76x2_get_rate_power(struct mt76x02_dev *dev, struct mt76_rate_power *t,
t->ht[12] = t->ht[13] = mt76x02_rate_power_val(val);
t->ht[14] = t->ht[15] = mt76x02_rate_power_val(val >> 8);
- val = mt76x02_eeprom_get(dev, MT_EE_TX_POWER_VHT_MCS0);
- t->vht[0] = t->vht[1] = mt76x02_rate_power_val(val);
- t->vht[2] = t->vht[3] = mt76x02_rate_power_val(val >> 8);
-
- val = mt76x02_eeprom_get(dev, MT_EE_TX_POWER_VHT_MCS4);
- t->vht[4] = t->vht[5] = mt76x02_rate_power_val(val);
- t->vht[6] = t->vht[7] = mt76x02_rate_power_val(val >> 8);
-
val = mt76x02_eeprom_get(dev, MT_EE_TX_POWER_VHT_MCS8);
if (!is_5ghz)
val >>= 8;
- t->vht[8] = t->vht[9] = mt76x02_rate_power_val(val >> 8);
-
- memcpy(t->stbc, t->ht, sizeof(t->stbc[0]) * 8);
- t->stbc[8] = t->vht[8];
- t->stbc[9] = t->vht[9];
+ t->vht[0] = t->vht[1] = mt76x02_rate_power_val(val >> 8);
}
EXPORT_SYMBOL_GPL(mt76x2_get_rate_power);
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.h b/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.h
index 3755632e6494..43430ef98b11 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.h
@@ -40,7 +40,7 @@ struct mt76x2_temp_comp {
unsigned int low_slope; /* J / dB */
};
-void mt76x2_get_rate_power(struct mt76x02_dev *dev, struct mt76_rate_power *t,
+void mt76x2_get_rate_power(struct mt76x02_dev *dev, struct mt76x02_rate_power *t,
struct ieee80211_channel *chan);
void mt76x2_get_power_info(struct mt76x02_dev *dev,
struct mt76x2_tx_power_info *t,
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/init.c b/drivers/net/wireless/mediatek/mt76/mt76x2/init.c
index 7b01a06d7f8d..19c139290adb 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2/init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2/init.c
@@ -182,7 +182,7 @@ void mt76x2_init_txpower(struct mt76x02_dev *dev,
{
struct ieee80211_channel *chan;
struct mt76x2_tx_power_info txp;
- struct mt76_rate_power t = {};
+ struct mt76x02_rate_power t = {};
int i;
for (i = 0; i < sband->n_channels; i++) {
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/phy.c b/drivers/net/wireless/mediatek/mt76/mt76x2/phy.c
index ed2dcb05d614..f84517d932dc 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2/phy.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2/phy.c
@@ -116,7 +116,7 @@ void mt76x2_phy_set_txpower_regs(struct mt76x02_dev *dev,
EXPORT_SYMBOL_GPL(mt76x2_phy_set_txpower_regs);
static int
-mt76x2_get_min_rate_power(struct mt76_rate_power *r)
+mt76x2_get_min_rate_power(struct mt76x02_rate_power *r)
{
int i;
s8 ret = 0;
@@ -140,7 +140,7 @@ void mt76x2_phy_set_txpower(struct mt76x02_dev *dev)
struct ieee80211_channel *chan = dev->mphy.chandef.chan;
struct mt76x2_tx_power_info txp;
int txp_0, txp_1, delta = 0;
- struct mt76_rate_power t = {};
+ struct mt76x02_rate_power t = {};
int base_power, gain;
mt76x2_get_power_info(dev, &txp, chan);
@@ -175,7 +175,7 @@ void mt76x2_phy_set_txpower(struct mt76x02_dev *dev)
dev->target_power = txp.target_power;
dev->target_power_delta[0] = txp_0 - txp.chain[0].target_power;
dev->target_power_delta[1] = txp_1 - txp.chain[0].target_power;
- dev->mt76.rate_power = t;
+ dev->rate_power = t;
mt76x02_phy_set_txpower(dev, txp_0, txp_1);
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/Kconfig b/drivers/net/wireless/mediatek/mt76/mt7915/Kconfig
index f21282cea845..d710726d47bf 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/Kconfig
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/Kconfig
@@ -2,6 +2,7 @@
config MT7915E
tristate "MediaTek MT7915E (PCIe) support"
select MT76_CONNAC_LIB
+ select WANT_DEV_COREDUMP
depends on MAC80211
depends on PCI
select RELAY
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/Makefile b/drivers/net/wireless/mediatek/mt76/mt7915/Makefile
index b794ceb79c37..797ae49805c3 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/Makefile
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/Makefile
@@ -6,4 +6,5 @@ mt7915e-y := pci.o init.o dma.o eeprom.o main.o mcu.o mac.o \
debugfs.o mmio.o
mt7915e-$(CONFIG_NL80211_TESTMODE) += testmode.o
-mt7915e-$(CONFIG_MT7986_WMAC) += soc.o \ No newline at end of file
+mt7915e-$(CONFIG_MT7986_WMAC) += soc.o
+mt7915e-$(CONFIG_DEV_COREDUMP) += coredump.o
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/coredump.c b/drivers/net/wireless/mediatek/mt76/mt7915/coredump.c
new file mode 100644
index 000000000000..d097a56dd33d
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/coredump.c
@@ -0,0 +1,410 @@
+// SPDX-License-Identifier: ISC
+/* Copyright (C) 2022 MediaTek Inc. */
+
+#include <linux/devcoredump.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/utsname.h>
+#include "coredump.h"
+
+static bool coredump_memdump;
+module_param(coredump_memdump, bool, 0644);
+MODULE_PARM_DESC(coredump_memdump, "Optional ability to dump firmware memory");
+
+static const struct mt7915_mem_region mt7915_mem_regions[] = {
+ {
+ .start = 0xe003b400,
+ .len = 0x00003bff,
+ .name = "CRAM",
+ },
+};
+
+static const struct mt7915_mem_region mt7916_mem_regions[] = {
+ {
+ .start = 0x00800000,
+ .len = 0x0005ffff,
+ .name = "ROM",
+ },
+ {
+ .start = 0x00900000,
+ .len = 0x00013fff,
+ .name = "ULM1",
+ },
+ {
+ .start = 0x02200000,
+ .len = 0x0004ffff,
+ .name = "ULM2",
+ },
+ {
+ .start = 0x02300000,
+ .len = 0x0004ffff,
+ .name = "ULM3",
+ },
+ {
+ .start = 0x00400000,
+ .len = 0x00027fff,
+ .name = "SRAM",
+ },
+ {
+ .start = 0xe0000000,
+ .len = 0x00157fff,
+ .name = "CRAM",
+ },
+};
+
+static const struct mt7915_mem_region mt7986_mem_regions[] = {
+ {
+ .start = 0x00800000,
+ .len = 0x0005ffff,
+ .name = "ROM",
+ },
+ {
+ .start = 0x00900000,
+ .len = 0x0000ffff,
+ .name = "ULM1",
+ },
+ {
+ .start = 0x02200000,
+ .len = 0x0004ffff,
+ .name = "ULM2",
+ },
+ {
+ .start = 0x02300000,
+ .len = 0x0004ffff,
+ .name = "ULM3",
+ },
+ {
+ .start = 0x00400000,
+ .len = 0x00017fff,
+ .name = "SRAM",
+ },
+ {
+ .start = 0xe0000000,
+ .len = 0x00113fff,
+ .name = "CRAM",
+ },
+};
+
+const struct mt7915_mem_region*
+mt7915_coredump_get_mem_layout(struct mt7915_dev *dev, u32 *num)
+{
+ switch (mt76_chip(&dev->mt76)) {
+ case 0x7915:
+ *num = ARRAY_SIZE(mt7915_mem_regions);
+ return &mt7915_mem_regions[0];
+ case 0x7986:
+ *num = ARRAY_SIZE(mt7986_mem_regions);
+ return &mt7986_mem_regions[0];
+ case 0x7916:
+ *num = ARRAY_SIZE(mt7916_mem_regions);
+ return &mt7916_mem_regions[0];
+ default:
+ return NULL;
+ }
+}
+
+static int mt7915_coredump_get_mem_size(struct mt7915_dev *dev)
+{
+ const struct mt7915_mem_region *mem_region;
+ size_t size = 0;
+ u32 num;
+ int i;
+
+ mem_region = mt7915_coredump_get_mem_layout(dev, &num);
+ if (!mem_region)
+ return 0;
+
+ for (i = 0; i < num; i++) {
+ size += mem_region->len;
+ mem_region++;
+ }
+
+ /* reserve space for the headers */
+ size += num * sizeof(struct mt7915_mem_hdr);
+ /* make sure it is aligned 4 bytes for debug message print out */
+ size = ALIGN(size, 4);
+
+ return size;
+}
+
+struct mt7915_crash_data *mt7915_coredump_new(struct mt7915_dev *dev)
+{
+ struct mt7915_crash_data *crash_data = dev->coredump.crash_data;
+
+ lockdep_assert_held(&dev->dump_mutex);
+
+ guid_gen(&crash_data->guid);
+ ktime_get_real_ts64(&crash_data->timestamp);
+
+ return crash_data;
+}
+
+static void
+mt7915_coredump_fw_state(struct mt7915_dev *dev, struct mt7915_coredump *dump,
+ bool *exception)
+{
+ u32 state, count, type;
+
+ type = (u32)mt76_get_field(dev, MT_FW_EXCEPT_TYPE, GENMASK(7, 0));
+ state = (u32)mt76_get_field(dev, MT_FW_ASSERT_STAT, GENMASK(7, 0));
+ count = is_mt7915(&dev->mt76) ?
+ (u32)mt76_get_field(dev, MT_FW_EXCEPT_COUNT, GENMASK(15, 8)) :
+ (u32)mt76_get_field(dev, MT_FW_EXCEPT_COUNT, GENMASK(7, 0));
+
+ /* normal mode: driver can manually trigger assert for detail info */
+ if (!count)
+ strscpy(dump->fw_state, "normal", sizeof(dump->fw_state));
+ else if (state > 1 && (count == 1) && type == 5)
+ strscpy(dump->fw_state, "assert", sizeof(dump->fw_state));
+ else if ((state > 1 && count == 1) || count > 1)
+ strscpy(dump->fw_state, "exception", sizeof(dump->fw_state));
+
+ *exception = !!count;
+}
+
+static void
+mt7915_coredump_fw_trace(struct mt7915_dev *dev, struct mt7915_coredump *dump,
+ bool exception)
+{
+ u32 n, irq, sch, base = MT_FW_EINT_INFO;
+
+ /* trap or run? */
+ dump->last_msg_id = mt76_rr(dev, MT_FW_LAST_MSG_ID);
+
+ n = is_mt7915(&dev->mt76) ?
+ (u32)mt76_get_field(dev, base, GENMASK(7, 0)) :
+ (u32)mt76_get_field(dev, base, GENMASK(15, 8));
+ dump->eint_info_idx = n;
+
+ irq = mt76_rr(dev, base + 0x8);
+ n = is_mt7915(&dev->mt76) ?
+ FIELD_GET(GENMASK(7, 0), irq) : FIELD_GET(GENMASK(23, 16), irq);
+ dump->irq_info_idx = n;
+
+ sch = mt76_rr(dev, MT_FW_SCHED_INFO);
+ n = is_mt7915(&dev->mt76) ?
+ FIELD_GET(GENMASK(7, 0), sch) : FIELD_GET(GENMASK(15, 8), sch);
+ dump->sched_info_idx = n;
+
+ if (exception) {
+ u32 i, y;
+
+ /* sched trace */
+ n = is_mt7915(&dev->mt76) ?
+ FIELD_GET(GENMASK(15, 8), sch) : FIELD_GET(GENMASK(7, 0), sch);
+ n = n > 60 ? 60 : n;
+
+ strscpy(dump->trace_sched, "(sched_info) id, time",
+ sizeof(dump->trace_sched));
+
+ for (y = dump->sched_info_idx, i = 0; i < n; i++, y++) {
+ mt7915_memcpy_fromio(dev, dump->sched, base + 0xc + y * 12,
+ sizeof(dump->sched));
+ y = y >= n ? 0 : y;
+ }
+
+ /* irq trace */
+ n = is_mt7915(&dev->mt76) ?
+ FIELD_GET(GENMASK(15, 8), irq) : FIELD_GET(GENMASK(7, 0), irq);
+ n = n > 60 ? 60 : n;
+
+ strscpy(dump->trace_irq, "(irq_info) id, time",
+ sizeof(dump->trace_irq));
+
+ for (y = dump->irq_info_idx, i = 0; i < n; i++, y++) {
+ mt7915_memcpy_fromio(dev, dump->irq, base + 0x4 + y * 16,
+ sizeof(dump->irq));
+ y = y >= n ? 0 : y;
+ }
+ }
+}
+
+static void
+mt7915_coredump_fw_stack(struct mt7915_dev *dev, struct mt7915_coredump *dump,
+ bool exception)
+{
+ u32 oldest, i, idx;
+
+ /* stop call stack record */
+ if (!exception)
+ mt76_clear(dev, 0x89050200, BIT(0));
+
+ oldest = (u32)mt76_get_field(dev, 0x89050200, GENMASK(20, 16)) + 2;
+ for (i = 0; i < 16; i++) {
+ idx = ((oldest + 2 * i + 1) % 32);
+ dump->call_stack[i] = mt76_rr(dev, 0x89050204 + idx * 4);
+ }
+
+ /* start call stack record */
+ if (!exception)
+ mt76_set(dev, 0x89050200, BIT(0));
+}
+
+static void
+mt7915_coredump_fw_task(struct mt7915_dev *dev, struct mt7915_coredump *dump)
+{
+ u32 offs = is_mt7915(&dev->mt76) ? 0xe0 : 0x170;
+
+ strscpy(dump->task_qid, "(task queue id) read, write",
+ sizeof(dump->task_qid));
+
+ dump->taskq[0].read = mt76_rr(dev, MT_FW_TASK_QID1);
+ dump->taskq[0].write = mt76_rr(dev, MT_FW_TASK_QID1 - 4);
+ dump->taskq[1].read = mt76_rr(dev, MT_FW_TASK_QID2);
+ dump->taskq[1].write = mt76_rr(dev, MT_FW_TASK_QID2 - 4);
+
+ strscpy(dump->task_info, "(task stack) start, end, size",
+ sizeof(dump->task_info));
+
+ dump->taski[0].start = mt76_rr(dev, MT_FW_TASK_START);
+ dump->taski[0].end = mt76_rr(dev, MT_FW_TASK_END);
+ dump->taski[0].size = mt76_rr(dev, MT_FW_TASK_SIZE);
+ dump->taski[1].start = mt76_rr(dev, MT_FW_TASK_START + offs);
+ dump->taski[1].end = mt76_rr(dev, MT_FW_TASK_END + offs);
+ dump->taski[1].size = mt76_rr(dev, MT_FW_TASK_SIZE + offs);
+}
+
+static void
+mt7915_coredump_fw_context(struct mt7915_dev *dev, struct mt7915_coredump *dump)
+{
+ u32 count, idx, id;
+
+ count = mt76_rr(dev, MT_FW_CIRQ_COUNT);
+
+ /* current context */
+ if (!count) {
+ strscpy(dump->fw_context, "(context) interrupt",
+ sizeof(dump->fw_context));
+
+ idx = is_mt7915(&dev->mt76) ?
+ (u32)mt76_get_field(dev, MT_FW_CIRQ_IDX, GENMASK(31, 16)) :
+ (u32)mt76_get_field(dev, MT_FW_CIRQ_IDX, GENMASK(15, 0));
+ dump->context.idx = idx;
+ dump->context.handler = mt76_rr(dev, MT_FW_CIRQ_LISR);
+ } else {
+ idx = mt76_rr(dev, MT_FW_TASK_IDX);
+ id = mt76_rr(dev, MT_FW_TASK_ID);
+
+ if (!id && idx == 3) {
+ strscpy(dump->fw_context, "(context) idle",
+ sizeof(dump->fw_context));
+ } else if (id && idx != 3) {
+ strscpy(dump->fw_context, "(context) task",
+ sizeof(dump->fw_context));
+
+ dump->context.idx = idx;
+ dump->context.handler = id;
+ }
+ }
+}
+
+static struct mt7915_coredump *mt7915_coredump_build(struct mt7915_dev *dev)
+{
+ struct mt7915_crash_data *crash_data = dev->coredump.crash_data;
+ struct mt7915_coredump *dump;
+ struct mt7915_coredump_mem *dump_mem;
+ size_t len, sofar = 0, hdr_len = sizeof(*dump);
+ unsigned char *buf;
+ bool exception;
+
+ len = hdr_len;
+
+ if (coredump_memdump && crash_data->memdump_buf_len)
+ len += sizeof(*dump_mem) + crash_data->memdump_buf_len;
+
+ sofar += hdr_len;
+
+ /* this is going to get big when we start dumping memory and such,
+ * so go ahead and use vmalloc.
+ */
+ buf = vzalloc(len);
+ if (!buf)
+ return NULL;
+
+ mutex_lock(&dev->dump_mutex);
+
+ dump = (struct mt7915_coredump *)(buf);
+ dump->len = len;
+
+ /* plain text */
+ strscpy(dump->magic, "mt76-crash-dump", sizeof(dump->magic));
+ strscpy(dump->kernel, init_utsname()->release, sizeof(dump->kernel));
+ strscpy(dump->fw_ver, dev->mt76.hw->wiphy->fw_version,
+ sizeof(dump->fw_ver));
+
+ guid_copy(&dump->guid, &crash_data->guid);
+ dump->tv_sec = crash_data->timestamp.tv_sec;
+ dump->tv_nsec = crash_data->timestamp.tv_nsec;
+ dump->device_id = mt76_chip(&dev->mt76);
+
+ mt7915_coredump_fw_state(dev, dump, &exception);
+ mt7915_coredump_fw_trace(dev, dump, exception);
+ mt7915_coredump_fw_task(dev, dump);
+ mt7915_coredump_fw_context(dev, dump);
+ mt7915_coredump_fw_stack(dev, dump, exception);
+
+ /* gather memory content */
+ dump_mem = (struct mt7915_coredump_mem *)(buf + sofar);
+ dump_mem->len = crash_data->memdump_buf_len;
+ if (coredump_memdump && crash_data->memdump_buf_len)
+ memcpy(dump_mem->data, crash_data->memdump_buf,
+ crash_data->memdump_buf_len);
+
+ mutex_unlock(&dev->dump_mutex);
+
+ return dump;
+}
+
+int mt7915_coredump_submit(struct mt7915_dev *dev)
+{
+ struct mt7915_coredump *dump;
+
+ dump = mt7915_coredump_build(dev);
+ if (!dump) {
+ dev_warn(dev->mt76.dev, "no crash dump data found\n");
+ return -ENODATA;
+ }
+
+ dev_coredumpv(dev->mt76.dev, dump, dump->len, GFP_KERNEL);
+
+ return 0;
+}
+
+int mt7915_coredump_register(struct mt7915_dev *dev)
+{
+ struct mt7915_crash_data *crash_data;
+
+ crash_data = vzalloc(sizeof(*dev->coredump.crash_data));
+ if (!crash_data)
+ return -ENOMEM;
+
+ dev->coredump.crash_data = crash_data;
+
+ if (coredump_memdump) {
+ crash_data->memdump_buf_len = mt7915_coredump_get_mem_size(dev);
+ if (!crash_data->memdump_buf_len)
+ /* no memory content */
+ return 0;
+
+ crash_data->memdump_buf = vzalloc(crash_data->memdump_buf_len);
+ if (!crash_data->memdump_buf) {
+ vfree(crash_data);
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+}
+
+void mt7915_coredump_unregister(struct mt7915_dev *dev)
+{
+ if (dev->coredump.crash_data->memdump_buf) {
+ vfree(dev->coredump.crash_data->memdump_buf);
+ dev->coredump.crash_data->memdump_buf = NULL;
+ dev->coredump.crash_data->memdump_buf_len = 0;
+ }
+
+ vfree(dev->coredump.crash_data);
+ dev->coredump.crash_data = NULL;
+}
+
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/coredump.h b/drivers/net/wireless/mediatek/mt76/mt7915/coredump.h
new file mode 100644
index 000000000000..709f8e9c795c
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/coredump.h
@@ -0,0 +1,136 @@
+/* SPDX-License-Identifier: ISC */
+/* Copyright (C) 2022 MediaTek Inc. */
+
+#ifndef _COREDUMP_H_
+#define _COREDUMP_H_
+
+#include "mt7915.h"
+
+struct trace {
+ u32 id;
+ u32 timestamp;
+};
+
+struct mt7915_coredump {
+ char magic[16];
+
+ u32 len;
+
+ guid_t guid;
+
+ /* time-of-day stamp */
+ u64 tv_sec;
+ /* time-of-day stamp, nano-seconds */
+ u64 tv_nsec;
+ /* kernel version */
+ char kernel[64];
+ /* firmware version */
+ char fw_ver[ETHTOOL_FWVERS_LEN];
+
+ u32 device_id;
+
+ /* exception state */
+ char fw_state[12];
+
+ u32 last_msg_id;
+ u32 eint_info_idx;
+ u32 irq_info_idx;
+ u32 sched_info_idx;
+
+ /* schedule info */
+ char trace_sched[32];
+ struct {
+ struct trace t;
+ u32 pc;
+ } sched[60];
+
+ /* irq info */
+ char trace_irq[32];
+ struct trace irq[60];
+
+ /* task queue status */
+ char task_qid[32];
+ struct {
+ u32 read;
+ u32 write;
+ } taskq[2];
+
+ /* task stack info */
+ char task_info[32];
+ struct {
+ u32 start;
+ u32 end;
+ u32 size;
+ } taski[2];
+
+ /* firmware context */
+ char fw_context[24];
+ struct {
+ u32 idx;
+ u32 handler;
+ } context;
+
+ /* link registers calltrace */
+ u32 call_stack[16];
+
+ /* memory content */
+ u8 data[];
+} __packed;
+
+struct mt7915_coredump_mem {
+ u32 len;
+ u8 data[];
+} __packed;
+
+struct mt7915_mem_hdr {
+ u32 start;
+ u32 len;
+ u8 data[];
+};
+
+struct mt7915_mem_region {
+ u32 start;
+ size_t len;
+
+ const char *name;
+};
+
+#ifdef CONFIG_DEV_COREDUMP
+
+const struct mt7915_mem_region *
+mt7915_coredump_get_mem_layout(struct mt7915_dev *dev, u32 *num);
+struct mt7915_crash_data *mt7915_coredump_new(struct mt7915_dev *dev);
+int mt7915_coredump_submit(struct mt7915_dev *dev);
+int mt7915_coredump_register(struct mt7915_dev *dev);
+void mt7915_coredump_unregister(struct mt7915_dev *dev);
+
+#else /* CONFIG_DEV_COREDUMP */
+
+static inline const struct mt7915_mem_region *
+mt7915_coredump_get_mem_layout(struct mt7915_dev *dev, u32 *num)
+{
+ return NULL;
+}
+
+static inline int mt7915_coredump_submit(struct mt7915_dev *dev)
+{
+ return 0;
+}
+
+static inline struct mt7915_crash_data *mt7915_coredump_new(struct mt7915_dev *dev)
+{
+ return NULL;
+}
+
+static inline int mt7915_coredump_register(struct mt7915_dev *dev)
+{
+ return 0;
+}
+
+static inline void mt7915_coredump_unregister(struct mt7915_dev *dev)
+{
+}
+
+#endif /* CONFIG_DEV_COREDUMP */
+
+#endif /* _COREDUMP_H_ */
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c
index 6ef3431cad64..fb46c2c1784f 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c
@@ -46,12 +46,12 @@ DEFINE_DEBUGFS_ATTRIBUTE(fops_implicit_txbf, mt7915_implicit_txbf_get,
/* test knob of system error recovery */
static ssize_t
-mt7915_fw_ser_set(struct file *file, const char __user *user_buf,
- size_t count, loff_t *ppos)
+mt7915_sys_recovery_set(struct file *file, const char __user *user_buf,
+ size_t count, loff_t *ppos)
{
struct mt7915_phy *phy = file->private_data;
struct mt7915_dev *dev = phy->dev;
- bool ext_phy = phy != &dev->phy;
+ bool band = phy->mt76->band_idx;
char buf[16];
int ret = 0;
u16 val;
@@ -71,9 +71,19 @@ mt7915_fw_ser_set(struct file *file, const char __user *user_buf,
return -EINVAL;
switch (val) {
+ /*
+ * 0: grab firmware current SER state.
+ * 1: trigger & enable system error L1 recovery.
+ * 2: trigger & enable system error L2 recovery.
+ * 3: trigger & enable system error L3 rx abort.
+ * 4: trigger & enable system error L3 tx abort
+ * 5: trigger & enable system error L3 tx disable.
+ * 6: trigger & enable system error L3 bf recovery.
+ * 7: trigger & enable system error full recovery.
+ * 8: trigger firmware crash.
+ */
case SER_QUERY:
- /* grab firmware SER stats */
- ret = mt7915_mcu_set_ser(dev, 0, 0, ext_phy);
+ ret = mt7915_mcu_set_ser(dev, 0, 0, band);
break;
case SER_SET_RECOVER_L1:
case SER_SET_RECOVER_L2:
@@ -81,11 +91,28 @@ mt7915_fw_ser_set(struct file *file, const char __user *user_buf,
case SER_SET_RECOVER_L3_TX_ABORT:
case SER_SET_RECOVER_L3_TX_DISABLE:
case SER_SET_RECOVER_L3_BF:
- ret = mt7915_mcu_set_ser(dev, SER_ENABLE, BIT(val), ext_phy);
+ ret = mt7915_mcu_set_ser(dev, SER_ENABLE, BIT(val), band);
+ if (ret)
+ return ret;
+
+ ret = mt7915_mcu_set_ser(dev, SER_RECOVER, val, band);
+ break;
+
+ /* enable full chip reset */
+ case SER_SET_RECOVER_FULL:
+ mt76_set(dev, MT_WFDMA0_MCU_HOST_INT_ENA, MT_MCU_CMD_WDT_MASK);
+ ret = mt7915_mcu_set_ser(dev, 1, 3, band);
if (ret)
return ret;
- ret = mt7915_mcu_set_ser(dev, SER_RECOVER, val, ext_phy);
+ dev->recovery.state |= MT_MCU_CMD_WDT_MASK;
+ mt7915_reset(dev);
+ break;
+
+ /* WARNING: trigger firmware crash */
+ case SER_SET_SYSTEM_ASSERT:
+ mt76_wr(dev, MT_MCU_WM_CIRQ_EINT_MASK_CLR_ADDR, BIT(18));
+ mt76_wr(dev, MT_MCU_WM_CIRQ_EINT_SOFT_ADDR, BIT(18));
break;
default:
break;
@@ -95,20 +122,45 @@ mt7915_fw_ser_set(struct file *file, const char __user *user_buf,
}
static ssize_t
-mt7915_fw_ser_get(struct file *file, char __user *user_buf,
- size_t count, loff_t *ppos)
+mt7915_sys_recovery_get(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
{
struct mt7915_phy *phy = file->private_data;
struct mt7915_dev *dev = phy->dev;
char *buff;
int desc = 0;
ssize_t ret;
- static const size_t bufsz = 400;
+ static const size_t bufsz = 1024;
buff = kmalloc(bufsz, GFP_KERNEL);
if (!buff)
return -ENOMEM;
+ /* HELP */
+ desc += scnprintf(buff + desc, bufsz - desc,
+ "Please echo the correct value ...\n");
+ desc += scnprintf(buff + desc, bufsz - desc,
+ "0: grab firmware transient SER state\n");
+ desc += scnprintf(buff + desc, bufsz - desc,
+ "1: trigger system error L1 recovery\n");
+ desc += scnprintf(buff + desc, bufsz - desc,
+ "2: trigger system error L2 recovery\n");
+ desc += scnprintf(buff + desc, bufsz - desc,
+ "3: trigger system error L3 rx abort\n");
+ desc += scnprintf(buff + desc, bufsz - desc,
+ "4: trigger system error L3 tx abort\n");
+ desc += scnprintf(buff + desc, bufsz - desc,
+ "5: trigger system error L3 tx disable\n");
+ desc += scnprintf(buff + desc, bufsz - desc,
+ "6: trigger system error L3 bf recovery\n");
+ desc += scnprintf(buff + desc, bufsz - desc,
+ "7: trigger system error full recovery\n");
+ desc += scnprintf(buff + desc, bufsz - desc,
+ "8: trigger firmware crash\n");
+
+ /* SER statistics */
+ desc += scnprintf(buff + desc, bufsz - desc,
+ "\nlet's dump firmware SER statistics...\n");
desc += scnprintf(buff + desc, bufsz - desc,
"::E R , SER_STATUS = 0x%08x\n",
mt76_rr(dev, MT_SWDEF_SER_STATS));
@@ -139,15 +191,19 @@ mt7915_fw_ser_get(struct file *file, char __user *user_buf,
desc += scnprintf(buff + desc, bufsz - desc,
"::E R , SER_LMAC_WISR7_B1 = 0x%08x\n",
mt76_rr(dev, MT_SWDEF_LAMC_WISR7_BN1_STATS));
+ desc += scnprintf(buff + desc, bufsz - desc,
+ "\nSYS_RESET_COUNT: WM %d, WA %d\n",
+ dev->recovery.wm_reset_count,
+ dev->recovery.wa_reset_count);
ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc);
kfree(buff);
return ret;
}
-static const struct file_operations mt7915_fw_ser_ops = {
- .write = mt7915_fw_ser_set,
- .read = mt7915_fw_ser_get,
+static const struct file_operations mt7915_sys_recovery_ops = {
+ .write = mt7915_sys_recovery_set,
+ .read = mt7915_sys_recovery_get,
.open = simple_open,
.llseek = default_llseek,
};
@@ -598,10 +654,6 @@ mt7915_fw_util_wm_show(struct seq_file *file, void *data)
struct mt7915_dev *dev = file->private;
seq_printf(file, "Program counter: 0x%x\n", mt76_rr(dev, MT_WM_MCU_PC));
- seq_printf(file, "Exception state: 0x%x\n",
- is_mt7915(&dev->mt76) ?
- (u32)mt76_get_field(dev, MT_FW_EXCEPTION, GENMASK(15, 8)) :
- (u32)mt76_get_field(dev, MT_FW_EXCEPTION, GENMASK(7, 0)));
if (dev->fw.debug_wm) {
seq_printf(file, "Busy: %u%% Peak busy: %u%%\n",
@@ -639,16 +691,17 @@ mt7915_ampdu_stat_read_phy(struct mt7915_phy *phy,
{
struct mt7915_dev *dev = phy->dev;
bool ext_phy = phy != &dev->phy;
- int bound[15], range[4], i, n;
+ int bound[15], range[4], i;
+ u8 band = phy->mt76->band_idx;
/* Tx ampdu stat */
for (i = 0; i < ARRAY_SIZE(range); i++)
- range[i] = mt76_rr(dev, MT_MIB_ARNG(phy->band_idx, i));
+ range[i] = mt76_rr(dev, MT_MIB_ARNG(band, i));
for (i = 0; i < ARRAY_SIZE(bound); i++)
bound[i] = MT_MIB_ARNCR_RANGE(range[i / 4], i % 4) + 1;
- seq_printf(file, "\nPhy %d, Phy band %d\n", ext_phy, phy->band_idx);
+ seq_printf(file, "\nPhy %d, Phy band %d\n", ext_phy, band);
seq_printf(file, "Length: %8d | ", bound[0]);
for (i = 0; i < ARRAY_SIZE(bound) - 1; i++)
@@ -656,9 +709,8 @@ mt7915_ampdu_stat_read_phy(struct mt7915_phy *phy,
bound[i] + 1, bound[i + 1]);
seq_puts(file, "\nCount: ");
- n = phy->band_idx ? ARRAY_SIZE(dev->mt76.aggr_stats) / 2 : 0;
for (i = 0; i < ARRAY_SIZE(bound); i++)
- seq_printf(file, "%8d | ", dev->mt76.aggr_stats[i + n]);
+ seq_printf(file, "%8d | ", phy->mt76->aggr_stats[i]);
seq_puts(file, "\n");
seq_printf(file, "BA miss count: %d\n", phy->mib.ba_miss_cnt);
@@ -906,35 +958,199 @@ mt7915_xmit_queues_show(struct seq_file *file, void *data)
DEFINE_SHOW_ATTRIBUTE(mt7915_xmit_queues);
-static int
-mt7915_rate_txpower_show(struct seq_file *file, void *data)
+#define mt7915_txpower_puts(prefix, rate) \
+({ \
+ len += scnprintf(buf + len, sz - len, "%-16s:", #prefix " (tmac)"); \
+ for (i = 0; i < mt7915_sku_group_len[rate]; i++, offs++) \
+ len += scnprintf(buf + len, sz - len, " %6d", txpwr[offs]); \
+ len += scnprintf(buf + len, sz - len, "\n"); \
+})
+
+#define mt7915_txpower_sets(rate, pwr, flag) \
+({ \
+ offs += len; \
+ len = mt7915_sku_group_len[rate]; \
+ if (mode == flag) { \
+ for (i = 0; i < len; i++) \
+ req.txpower_sku[offs + i] = pwr; \
+ } \
+})
+
+static ssize_t
+mt7915_rate_txpower_get(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct mt7915_phy *phy = file->private_data;
+ struct mt7915_dev *dev = phy->dev;
+ s8 txpwr[MT7915_SKU_RATE_NUM];
+ static const size_t sz = 2048;
+ u8 band = phy->mt76->band_idx;
+ int i, offs = 0, len = 0;
+ ssize_t ret;
+ char *buf;
+ u32 reg;
+
+ buf = kzalloc(sz, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = mt7915_mcu_get_txpower_sku(phy, txpwr, sizeof(txpwr));
+ if (ret)
+ return ret;
+
+ /* Txpower propagation path: TMAC -> TXV -> BBP */
+ len += scnprintf(buf + len, sz - len,
+ "\nPhy%d Tx power table (channel %d)\n",
+ phy != &dev->phy, phy->mt76->chandef.chan->hw_value);
+ len += scnprintf(buf + len, sz - len, "%-16s %6s %6s %6s %6s\n",
+ " ", "1m", "2m", "5m", "11m");
+ mt7915_txpower_puts(CCK, SKU_CCK);
+
+ len += scnprintf(buf + len, sz - len,
+ "%-16s %6s %6s %6s %6s %6s %6s %6s %6s\n",
+ " ", "6m", "9m", "12m", "18m", "24m", "36m", "48m",
+ "54m");
+ mt7915_txpower_puts(OFDM, SKU_OFDM);
+
+ len += scnprintf(buf + len, sz - len,
+ "%-16s %6s %6s %6s %6s %6s %6s %6s %6s\n",
+ " ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4",
+ "mcs5", "mcs6", "mcs7");
+ mt7915_txpower_puts(HT20, SKU_HT_BW20);
+
+ len += scnprintf(buf + len, sz - len,
+ "%-16s %6s %6s %6s %6s %6s %6s %6s %6s %6s\n",
+ " ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4", "mcs5",
+ "mcs6", "mcs7", "mcs32");
+ mt7915_txpower_puts(HT40, SKU_HT_BW40);
+
+ len += scnprintf(buf + len, sz - len,
+ "%-16s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s\n",
+ " ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4", "mcs5",
+ "mcs6", "mcs7", "mcs8", "mcs9", "mcs10", "mcs11");
+ mt7915_txpower_puts(VHT20, SKU_VHT_BW20);
+ mt7915_txpower_puts(VHT40, SKU_VHT_BW40);
+ mt7915_txpower_puts(VHT80, SKU_VHT_BW80);
+ mt7915_txpower_puts(VHT160, SKU_VHT_BW160);
+ mt7915_txpower_puts(HE26, SKU_HE_RU26);
+ mt7915_txpower_puts(HE52, SKU_HE_RU52);
+ mt7915_txpower_puts(HE106, SKU_HE_RU106);
+ mt7915_txpower_puts(HE242, SKU_HE_RU242);
+ mt7915_txpower_puts(HE484, SKU_HE_RU484);
+ mt7915_txpower_puts(HE996, SKU_HE_RU996);
+ mt7915_txpower_puts(HE996x2, SKU_HE_RU2x996);
+
+ reg = is_mt7915(&dev->mt76) ? MT_WF_PHY_TPC_CTRL_STAT(band) :
+ MT_WF_PHY_TPC_CTRL_STAT_MT7916(band);
+
+ len += scnprintf(buf + len, sz - len, "\nTx power (bbp) : %6ld\n",
+ mt76_get_field(dev, reg, MT_WF_PHY_TPC_POWER));
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+ kfree(buf);
+ return ret;
+}
+
+static ssize_t
+mt7915_rate_txpower_set(struct file *file, const char __user *user_buf,
+ size_t count, loff_t *ppos)
{
- static const char * const sku_group_name[] = {
- "CCK", "OFDM", "HT20", "HT40",
- "VHT20", "VHT40", "VHT80", "VHT160",
- "RU26", "RU52", "RU106", "RU242/SU20",
- "RU484/SU40", "RU996/SU80", "RU2x996/SU160"
+ struct mt7915_phy *phy = file->private_data;
+ struct mt7915_dev *dev = phy->dev;
+ struct mt76_phy *mphy = phy->mt76;
+ struct mt7915_mcu_txpower_sku req = {
+ .format_id = TX_POWER_LIMIT_TABLE,
+ .band_idx = phy->mt76->band_idx,
};
- struct mt7915_phy *phy = file->private;
- s8 txpower[MT7915_SKU_RATE_NUM], *buf;
- int i;
+ char buf[100];
+ int i, ret, pwr160 = 0, pwr80 = 0, pwr40 = 0, pwr20 = 0;
+ enum mac80211_rx_encoding mode;
+ u32 offs = 0, len = 0;
- seq_printf(file, "\nBand %d\n", phy != &phy->dev->phy);
- mt7915_mcu_get_txpower_sku(phy, txpower, sizeof(txpower));
- for (i = 0, buf = txpower; i < ARRAY_SIZE(mt7915_sku_group_len); i++) {
- u8 mcs_num = mt7915_sku_group_len[i];
+ if (count >= sizeof(buf))
+ return -EINVAL;
- if (i >= SKU_VHT_BW20 && i <= SKU_VHT_BW160)
- mcs_num = 10;
+ if (copy_from_user(buf, user_buf, count))
+ return -EFAULT;
- mt76_seq_puts_array(file, sku_group_name[i], buf, mcs_num);
- buf += mt7915_sku_group_len[i];
+ if (count && buf[count - 1] == '\n')
+ buf[count - 1] = '\0';
+ else
+ buf[count] = '\0';
+
+ if (sscanf(buf, "%u %u %u %u %u",
+ &mode, &pwr160, &pwr80, &pwr40, &pwr20) != 5) {
+ dev_warn(dev->mt76.dev,
+ "per bandwidth power limit: Mode BW160 BW80 BW40 BW20");
+ return -EINVAL;
}
- return 0;
+ if (mode > RX_ENC_HE)
+ return -EINVAL;
+
+ if (pwr160)
+ pwr160 = mt7915_get_power_bound(phy, pwr160);
+ if (pwr80)
+ pwr80 = mt7915_get_power_bound(phy, pwr80);
+ if (pwr40)
+ pwr40 = mt7915_get_power_bound(phy, pwr40);
+ if (pwr20)
+ pwr20 = mt7915_get_power_bound(phy, pwr20);
+
+ if (pwr160 < 0 || pwr80 < 0 || pwr40 < 0 || pwr20 < 0)
+ return -EINVAL;
+
+ mutex_lock(&dev->mt76.mutex);
+ ret = mt7915_mcu_get_txpower_sku(phy, req.txpower_sku,
+ sizeof(req.txpower_sku));
+ if (ret)
+ goto out;
+
+ mt7915_txpower_sets(SKU_CCK, pwr20, RX_ENC_LEGACY);
+ mt7915_txpower_sets(SKU_OFDM, pwr20, RX_ENC_LEGACY);
+ if (mode == RX_ENC_LEGACY)
+ goto skip;
+
+ mt7915_txpower_sets(SKU_HT_BW20, pwr20, RX_ENC_HT);
+ mt7915_txpower_sets(SKU_HT_BW40, pwr40, RX_ENC_HT);
+ if (mode == RX_ENC_HT)
+ goto skip;
+
+ mt7915_txpower_sets(SKU_VHT_BW20, pwr20, RX_ENC_VHT);
+ mt7915_txpower_sets(SKU_VHT_BW40, pwr40, RX_ENC_VHT);
+ mt7915_txpower_sets(SKU_VHT_BW80, pwr80, RX_ENC_VHT);
+ mt7915_txpower_sets(SKU_VHT_BW160, pwr160, RX_ENC_VHT);
+ if (mode == RX_ENC_VHT)
+ goto skip;
+
+ mt7915_txpower_sets(SKU_HE_RU26, pwr20, RX_ENC_HE + 1);
+ mt7915_txpower_sets(SKU_HE_RU52, pwr20, RX_ENC_HE + 1);
+ mt7915_txpower_sets(SKU_HE_RU106, pwr20, RX_ENC_HE + 1);
+ mt7915_txpower_sets(SKU_HE_RU242, pwr20, RX_ENC_HE);
+ mt7915_txpower_sets(SKU_HE_RU484, pwr40, RX_ENC_HE);
+ mt7915_txpower_sets(SKU_HE_RU996, pwr80, RX_ENC_HE);
+ mt7915_txpower_sets(SKU_HE_RU2x996, pwr160, RX_ENC_HE);
+skip:
+ ret = mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(TX_POWER_FEATURE_CTRL),
+ &req, sizeof(req), true);
+ if (ret)
+ goto out;
+
+ mphy->txpower_cur = max(mphy->txpower_cur,
+ max(pwr160, max(pwr80, max(pwr40, pwr20))));
+out:
+ mutex_unlock(&dev->mt76.mutex);
+
+ return ret ? ret : count;
}
-DEFINE_SHOW_ATTRIBUTE(mt7915_rate_txpower);
+static const struct file_operations mt7915_rate_txpower_fops = {
+ .write = mt7915_rate_txpower_set,
+ .read = mt7915_rate_txpower_get,
+ .open = simple_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
static int
mt7915_twt_stats(struct seq_file *s, void *data)
@@ -963,7 +1179,7 @@ mt7915_twt_stats(struct seq_file *s, void *data)
}
/* The index of RF registers use the generic regidx, combined with two parts:
- * WF selection [31:28] and offset [27:0].
+ * WF selection [31:24] and offset [23:0].
*/
static int
mt7915_rf_regval_get(void *data, u64 *val)
@@ -1010,7 +1226,8 @@ int mt7915_init_debugfs(struct mt7915_phy *phy)
debugfs_create_file("xmit-queues", 0400, dir, phy,
&mt7915_xmit_queues_fops);
debugfs_create_file("tx_stats", 0400, dir, phy, &mt7915_tx_stats_fops);
- debugfs_create_file("fw_ser", 0600, dir, phy, &mt7915_fw_ser_ops);
+ debugfs_create_file("sys_recovery", 0600, dir, phy,
+ &mt7915_sys_recovery_ops);
debugfs_create_file("fw_debug_wm", 0600, dir, dev, &fops_fw_debug_wm);
debugfs_create_file("fw_debug_wa", 0600, dir, dev, &fops_fw_debug_wa);
debugfs_create_file("fw_debug_bin", 0600, dir, dev, &fops_fw_debug_bin);
@@ -1026,7 +1243,7 @@ int mt7915_init_debugfs(struct mt7915_phy *phy)
mt7915_twt_stats);
debugfs_create_file("rf_regval", 0600, dir, dev, &fops_rf_regval);
- if (!dev->dbdc_support || phy->band_idx) {
+ 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,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/dma.c b/drivers/net/wireless/mediatek/mt76/mt7915/dma.c
index 00aafc2422f3..e3fa064918bf 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/dma.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/dma.c
@@ -11,7 +11,11 @@ mt7915_init_tx_queues(struct mt7915_phy *phy, int idx, int n_desc, int ring_base
struct mt7915_dev *dev = phy->dev;
if (mtk_wed_device_active(&phy->dev->mt76.mmio.wed)) {
- ring_base = MT_WED_TX_RING_BASE;
+ if (is_mt7986(&dev->mt76))
+ ring_base += MT_TXQ_ID(0) * MT_RING_SIZE;
+ else
+ ring_base = MT_WED_TX_RING_BASE;
+
idx -= MT_TXQ_ID(0);
}
@@ -46,29 +50,65 @@ static void mt7915_dma_config(struct mt7915_dev *dev)
#define TXQ_CONFIG(q, wfdma, int, id) Q_CONFIG(__TXQ(q), (wfdma), (int), (id))
if (is_mt7915(&dev->mt76)) {
- RXQ_CONFIG(MT_RXQ_MAIN, WFDMA0, MT_INT_RX_DONE_BAND0, MT7915_RXQ_BAND0);
- RXQ_CONFIG(MT_RXQ_MCU, WFDMA1, MT_INT_RX_DONE_WM, MT7915_RXQ_MCU_WM);
- RXQ_CONFIG(MT_RXQ_MCU_WA, WFDMA1, MT_INT_RX_DONE_WA, MT7915_RXQ_MCU_WA);
- RXQ_CONFIG(MT_RXQ_BAND1, WFDMA0, MT_INT_RX_DONE_BAND1, MT7915_RXQ_BAND1);
- RXQ_CONFIG(MT_RXQ_BAND1_WA, WFDMA1, MT_INT_RX_DONE_WA_EXT, MT7915_RXQ_MCU_WA_EXT);
- RXQ_CONFIG(MT_RXQ_MAIN_WA, WFDMA1, MT_INT_RX_DONE_WA_MAIN, MT7915_RXQ_MCU_WA);
+ RXQ_CONFIG(MT_RXQ_MAIN, WFDMA0, MT_INT_RX_DONE_BAND0,
+ MT7915_RXQ_BAND0);
+ RXQ_CONFIG(MT_RXQ_MCU, WFDMA1, MT_INT_RX_DONE_WM,
+ MT7915_RXQ_MCU_WM);
+ RXQ_CONFIG(MT_RXQ_MCU_WA, WFDMA1, MT_INT_RX_DONE_WA,
+ MT7915_RXQ_MCU_WA);
+ RXQ_CONFIG(MT_RXQ_BAND1, WFDMA0, MT_INT_RX_DONE_BAND1,
+ MT7915_RXQ_BAND1);
+ RXQ_CONFIG(MT_RXQ_BAND1_WA, WFDMA1, MT_INT_RX_DONE_WA_EXT,
+ MT7915_RXQ_MCU_WA_EXT);
+ RXQ_CONFIG(MT_RXQ_MAIN_WA, WFDMA1, MT_INT_RX_DONE_WA_MAIN,
+ MT7915_RXQ_MCU_WA);
TXQ_CONFIG(0, WFDMA1, MT_INT_TX_DONE_BAND0, MT7915_TXQ_BAND0);
TXQ_CONFIG(1, WFDMA1, MT_INT_TX_DONE_BAND1, MT7915_TXQ_BAND1);
- MCUQ_CONFIG(MT_MCUQ_WM, WFDMA1, MT_INT_TX_DONE_MCU_WM, MT7915_TXQ_MCU_WM);
- MCUQ_CONFIG(MT_MCUQ_WA, WFDMA1, MT_INT_TX_DONE_MCU_WA, MT7915_TXQ_MCU_WA);
- MCUQ_CONFIG(MT_MCUQ_FWDL, WFDMA1, MT_INT_TX_DONE_FWDL, MT7915_TXQ_FWDL);
+ MCUQ_CONFIG(MT_MCUQ_WM, WFDMA1, MT_INT_TX_DONE_MCU_WM,
+ MT7915_TXQ_MCU_WM);
+ MCUQ_CONFIG(MT_MCUQ_WA, WFDMA1, MT_INT_TX_DONE_MCU_WA,
+ MT7915_TXQ_MCU_WA);
+ MCUQ_CONFIG(MT_MCUQ_FWDL, WFDMA1, MT_INT_TX_DONE_FWDL,
+ MT7915_TXQ_FWDL);
} else {
- RXQ_CONFIG(MT_RXQ_MAIN, WFDMA0, MT_INT_RX_DONE_BAND0_MT7916, MT7916_RXQ_BAND0);
- RXQ_CONFIG(MT_RXQ_MCU, WFDMA0, MT_INT_RX_DONE_WM, MT7916_RXQ_MCU_WM);
- RXQ_CONFIG(MT_RXQ_MCU_WA, WFDMA0, MT_INT_RX_DONE_WA, MT7916_RXQ_MCU_WA);
- RXQ_CONFIG(MT_RXQ_BAND1, WFDMA0, MT_INT_RX_DONE_BAND1_MT7916, MT7916_RXQ_BAND1);
- RXQ_CONFIG(MT_RXQ_BAND1_WA, WFDMA0, MT_INT_RX_DONE_WA_EXT_MT7916, MT7916_RXQ_MCU_WA_EXT);
- RXQ_CONFIG(MT_RXQ_MAIN_WA, WFDMA0, MT_INT_RX_DONE_WA_MAIN_MT7916, MT7916_RXQ_MCU_WA_MAIN);
- TXQ_CONFIG(0, WFDMA0, MT_INT_TX_DONE_BAND0, MT7915_TXQ_BAND0);
- TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND1, MT7915_TXQ_BAND1);
- MCUQ_CONFIG(MT_MCUQ_WM, WFDMA0, MT_INT_TX_DONE_MCU_WM, MT7915_TXQ_MCU_WM);
- MCUQ_CONFIG(MT_MCUQ_WA, WFDMA0, MT_INT_TX_DONE_MCU_WA_MT7916, MT7915_TXQ_MCU_WA);
- MCUQ_CONFIG(MT_MCUQ_FWDL, WFDMA0, MT_INT_TX_DONE_FWDL, MT7915_TXQ_FWDL);
+ RXQ_CONFIG(MT_RXQ_MCU, WFDMA0, MT_INT_RX_DONE_WM,
+ MT7916_RXQ_MCU_WM);
+ RXQ_CONFIG(MT_RXQ_BAND1_WA, WFDMA0, MT_INT_RX_DONE_WA_EXT_MT7916,
+ MT7916_RXQ_MCU_WA_EXT);
+ MCUQ_CONFIG(MT_MCUQ_WM, WFDMA0, MT_INT_TX_DONE_MCU_WM,
+ MT7915_TXQ_MCU_WM);
+ MCUQ_CONFIG(MT_MCUQ_WA, WFDMA0, MT_INT_TX_DONE_MCU_WA_MT7916,
+ MT7915_TXQ_MCU_WA);
+ MCUQ_CONFIG(MT_MCUQ_FWDL, WFDMA0, MT_INT_TX_DONE_FWDL,
+ MT7915_TXQ_FWDL);
+
+ if (is_mt7916(&dev->mt76) && mtk_wed_device_active(&dev->mt76.mmio.wed)) {
+ RXQ_CONFIG(MT_RXQ_MAIN, WFDMA0, MT_INT_WED_RX_DONE_BAND0_MT7916,
+ MT7916_RXQ_BAND0);
+ RXQ_CONFIG(MT_RXQ_MCU_WA, WFDMA0, MT_INT_WED_RX_DONE_WA_MT7916,
+ MT7916_RXQ_MCU_WA);
+ RXQ_CONFIG(MT_RXQ_BAND1, WFDMA0, MT_INT_WED_RX_DONE_BAND1_MT7916,
+ MT7916_RXQ_BAND1);
+ RXQ_CONFIG(MT_RXQ_MAIN_WA, WFDMA0, MT_INT_WED_RX_DONE_WA_MAIN_MT7916,
+ MT7916_RXQ_MCU_WA_MAIN);
+ TXQ_CONFIG(0, WFDMA0, MT_INT_WED_TX_DONE_BAND0,
+ MT7915_TXQ_BAND0);
+ TXQ_CONFIG(1, WFDMA0, MT_INT_WED_TX_DONE_BAND1,
+ MT7915_TXQ_BAND1);
+ } else {
+ RXQ_CONFIG(MT_RXQ_MAIN, WFDMA0, MT_INT_RX_DONE_BAND0_MT7916,
+ MT7916_RXQ_BAND0);
+ RXQ_CONFIG(MT_RXQ_MCU_WA, WFDMA0, MT_INT_RX_DONE_WA,
+ MT7916_RXQ_MCU_WA);
+ RXQ_CONFIG(MT_RXQ_BAND1, WFDMA0, MT_INT_RX_DONE_BAND1_MT7916,
+ MT7916_RXQ_BAND1);
+ RXQ_CONFIG(MT_RXQ_MAIN_WA, WFDMA0, MT_INT_RX_DONE_WA_MAIN_MT7916,
+ MT7916_RXQ_MCU_WA_MAIN);
+ TXQ_CONFIG(0, WFDMA0, MT_INT_TX_DONE_BAND0,
+ MT7915_TXQ_BAND0);
+ TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND1,
+ MT7915_TXQ_BAND1);
+ }
}
}
@@ -313,17 +353,26 @@ static int mt7915_dma_enable(struct mt7915_dev *dev)
MT_INT_TX_DONE_MCU |
MT_INT_MCU_CMD;
- if (!dev->phy.band_idx)
+ if (!dev->phy.mt76->band_idx)
irq_mask |= MT_INT_BAND0_RX_DONE;
- if (dev->dbdc_support || dev->phy.band_idx)
+ if (dev->dbdc_support || dev->phy.mt76->band_idx)
irq_mask |= MT_INT_BAND1_RX_DONE;
if (mtk_wed_device_active(&dev->mt76.mmio.wed)) {
u32 wed_irq_mask = irq_mask;
+ int ret;
wed_irq_mask |= MT_INT_TX_DONE_BAND0 | MT_INT_TX_DONE_BAND1;
- mt76_wr(dev, MT_INT_WED_MASK_CSR, wed_irq_mask);
+ if (!is_mt7986(&dev->mt76))
+ mt76_wr(dev, MT_INT_WED_MASK_CSR, wed_irq_mask);
+ else
+ mt76_wr(dev, MT_INT_MASK_CSR, wed_irq_mask);
+
+ ret = mt7915_mcu_wed_enable_rx_stats(dev);
+ if (ret)
+ return ret;
+
mtk_wed_device_start(&dev->mt76.mmio.wed, wed_irq_mask);
}
@@ -348,20 +397,28 @@ int mt7915_dma_init(struct mt7915_dev *dev, struct mt7915_phy *phy2)
mt7915_dma_disable(dev, true);
- if (mtk_wed_device_active(&dev->mt76.mmio.wed)) {
- mt76_set(dev, MT_WFDMA_HOST_CONFIG, MT_WFDMA_HOST_CONFIG_WED);
-
- mt76_wr(dev, MT_WFDMA_WED_RING_CONTROL,
- FIELD_PREP(MT_WFDMA_WED_RING_CONTROL_TX0, 18) |
- FIELD_PREP(MT_WFDMA_WED_RING_CONTROL_TX1, 19) |
- FIELD_PREP(MT_WFDMA_WED_RING_CONTROL_RX1, 1));
+ if (mtk_wed_device_active(&mdev->mmio.wed)) {
+ if (!is_mt7986(mdev)) {
+ u8 wed_control_rx1 = is_mt7915(mdev) ? 1 : 2;
+
+ mt76_set(dev, MT_WFDMA_HOST_CONFIG,
+ MT_WFDMA_HOST_CONFIG_WED);
+ mt76_wr(dev, MT_WFDMA_WED_RING_CONTROL,
+ FIELD_PREP(MT_WFDMA_WED_RING_CONTROL_TX0, 18) |
+ FIELD_PREP(MT_WFDMA_WED_RING_CONTROL_TX1, 19) |
+ FIELD_PREP(MT_WFDMA_WED_RING_CONTROL_RX1,
+ wed_control_rx1));
+ if (is_mt7915(mdev))
+ mt76_rmw(dev, MT_WFDMA0_EXT0_CFG, MT_WFDMA0_EXT0_RXWB_KEEP,
+ MT_WFDMA0_EXT0_RXWB_KEEP);
+ }
} else {
mt76_clear(dev, MT_WFDMA_HOST_CONFIG, MT_WFDMA_HOST_CONFIG_WED);
}
/* init tx queue */
ret = mt7915_init_tx_queues(&dev->phy,
- MT_TXQ_ID(dev->phy.band_idx),
+ MT_TXQ_ID(dev->phy.mt76->band_idx),
MT7915_TX_RING_SIZE,
MT_TXQ_RING_BASE(0));
if (ret)
@@ -369,7 +426,7 @@ int mt7915_dma_init(struct mt7915_dev *dev, struct mt7915_phy *phy2)
if (phy2) {
ret = mt7915_init_tx_queues(phy2,
- MT_TXQ_ID(phy2->band_idx),
+ MT_TXQ_ID(phy2->mt76->band_idx),
MT7915_TX_RING_SIZE,
MT_TXQ_RING_BASE(1));
if (ret)
@@ -410,7 +467,7 @@ int mt7915_dma_init(struct mt7915_dev *dev, struct mt7915_phy *phy2)
return ret;
/* event from WA */
- if (mtk_wed_device_active(&dev->mt76.mmio.wed)) {
+ if (mtk_wed_device_active(&mdev->mmio.wed) && is_mt7915(mdev)) {
wa_rx_base = MT_WED_RX_RING_BASE;
wa_rx_idx = MT7915_RXQ_MCU_WA;
dev->mt76.q_rx[MT_RXQ_MCU_WA].flags = MT_WED_Q_TXFREE;
@@ -425,7 +482,14 @@ int mt7915_dma_init(struct mt7915_dev *dev, struct mt7915_phy *phy2)
return ret;
/* rx data queue for band0 */
- if (!dev->phy.band_idx) {
+ if (!dev->phy.mt76->band_idx) {
+ if (mtk_wed_device_active(&mdev->mmio.wed) &&
+ mtk_wed_get_rx_capa(&mdev->mmio.wed)) {
+ dev->mt76.q_rx[MT_RXQ_MAIN].flags =
+ MT_WED_Q_RX(MT7915_RXQ_BAND0);
+ dev->mt76.rx_token_size += MT7915_RX_RING_SIZE;
+ }
+
ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MAIN],
MT_RXQ_ID(MT_RXQ_MAIN),
MT7915_RX_RING_SIZE,
@@ -437,16 +501,32 @@ int mt7915_dma_init(struct mt7915_dev *dev, struct mt7915_phy *phy2)
/* tx free notify event from WA for band0 */
if (!is_mt7915(mdev)) {
+ wa_rx_base = MT_RXQ_RING_BASE(MT_RXQ_MAIN_WA);
+ wa_rx_idx = MT_RXQ_ID(MT_RXQ_MAIN_WA);
+
+ if (mtk_wed_device_active(&mdev->mmio.wed)) {
+ mdev->q_rx[MT_RXQ_MAIN_WA].flags = MT_WED_Q_TXFREE;
+ if (is_mt7916(mdev)) {
+ wa_rx_base = MT_WED_RX_RING_BASE;
+ wa_rx_idx = MT7915_RXQ_MCU_WA;
+ }
+ }
+
ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MAIN_WA],
- MT_RXQ_ID(MT_RXQ_MAIN_WA),
- MT7915_RX_MCU_RING_SIZE,
- MT_RX_BUF_SIZE,
- MT_RXQ_RING_BASE(MT_RXQ_MAIN_WA));
+ wa_rx_idx, MT7915_RX_MCU_RING_SIZE,
+ MT_RX_BUF_SIZE, wa_rx_base);
if (ret)
return ret;
}
- if (dev->dbdc_support || dev->phy.band_idx) {
+ if (dev->dbdc_support || dev->phy.mt76->band_idx) {
+ if (mtk_wed_device_active(&mdev->mmio.wed) &&
+ mtk_wed_get_rx_capa(&mdev->mmio.wed)) {
+ dev->mt76.q_rx[MT_RXQ_BAND1].flags =
+ MT_WED_Q_RX(MT7915_RXQ_BAND1);
+ dev->mt76.rx_token_size += MT7915_RX_RING_SIZE;
+ }
+
/* rx data queue for band1 */
ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_BAND1],
MT_RXQ_ID(MT_RXQ_BAND1),
@@ -479,6 +559,53 @@ int mt7915_dma_init(struct mt7915_dev *dev, struct mt7915_phy *phy2)
return 0;
}
+int mt7915_dma_reset(struct mt7915_dev *dev, bool force)
+{
+ struct mt76_phy *mphy_ext = dev->mt76.phys[MT_BAND1];
+ int i;
+
+ /* clean up hw queues */
+ for (i = 0; i < ARRAY_SIZE(dev->mt76.phy.q_tx); i++) {
+ mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[i], true);
+ if (mphy_ext)
+ mt76_queue_tx_cleanup(dev, mphy_ext->q_tx[i], true);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(dev->mt76.q_mcu); i++)
+ mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[i], true);
+
+ mt76_for_each_q_rx(&dev->mt76, i)
+ mt76_queue_rx_cleanup(dev, &dev->mt76.q_rx[i]);
+
+ /* reset wfsys */
+ if (force)
+ mt7915_wfsys_reset(dev);
+
+ mt7915_dma_disable(dev, force);
+
+ /* reset hw queues */
+ for (i = 0; i < __MT_TXQ_MAX; i++) {
+ mt76_queue_reset(dev, dev->mphy.q_tx[i]);
+ if (mphy_ext)
+ mt76_queue_reset(dev, mphy_ext->q_tx[i]);
+ }
+
+ for (i = 0; i < __MT_MCUQ_MAX; i++)
+ mt76_queue_reset(dev, dev->mt76.q_mcu[i]);
+
+ mt76_for_each_q_rx(&dev->mt76, i)
+ mt76_queue_reset(dev, &dev->mt76.q_rx[i]);
+
+ mt76_tx_status_check(&dev->mt76, true);
+
+ mt7915_dma_enable(dev);
+
+ mt76_for_each_q_rx(&dev->mt76, i)
+ mt76_queue_rx_reset(dev, i);
+
+ return 0;
+}
+
void mt7915_dma_cleanup(struct mt7915_dev *dev)
{
mt7915_dma_disable(dev, true);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c
index 4b1a9811646f..59069fb86414 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c
@@ -131,9 +131,10 @@ static void mt7915_eeprom_parse_band_config(struct mt7915_phy *phy)
{
struct mt7915_dev *dev = phy->dev;
u8 *eeprom = dev->mt76.eeprom.data;
+ u8 band = phy->mt76->band_idx;
u32 val;
- val = eeprom[MT_EE_WIFI_CONF + phy->band_idx];
+ val = eeprom[MT_EE_WIFI_CONF + band];
val = FIELD_GET(MT_EE_WIFI_CONF0_BAND_SEL, val);
if (!is_mt7915(&dev->mt76)) {
@@ -153,7 +154,7 @@ static void mt7915_eeprom_parse_band_config(struct mt7915_phy *phy)
return;
}
} else if (val == MT_EE_BAND_SEL_DEFAULT && dev->dbdc_support) {
- val = phy->band_idx ? MT_EE_BAND_SEL_5GHZ : MT_EE_BAND_SEL_2GHZ;
+ val = band ? MT_EE_BAND_SEL_5GHZ : MT_EE_BAND_SEL_2GHZ;
}
switch (val) {
@@ -173,60 +174,51 @@ static void mt7915_eeprom_parse_band_config(struct mt7915_phy *phy)
void mt7915_eeprom_parse_hw_cap(struct mt7915_dev *dev,
struct mt7915_phy *phy)
{
- u8 nss, nss_band, nss_band_max, *eeprom = dev->mt76.eeprom.data;
+ u8 path, nss, nss_max = 4, *eeprom = dev->mt76.eeprom.data;
struct mt76_phy *mphy = phy->mt76;
- bool ext_phy = phy != &dev->phy;
+ u8 band = phy->mt76->band_idx;
mt7915_eeprom_parse_band_config(phy);
- /* read tx/rx mask from eeprom */
+ /* read tx/rx path from eeprom */
if (is_mt7915(&dev->mt76)) {
- nss = FIELD_GET(MT_EE_WIFI_CONF0_TX_PATH,
- eeprom[MT_EE_WIFI_CONF]);
+ path = FIELD_GET(MT_EE_WIFI_CONF0_TX_PATH,
+ eeprom[MT_EE_WIFI_CONF]);
} else {
- nss = FIELD_GET(MT_EE_WIFI_CONF0_TX_PATH,
- eeprom[MT_EE_WIFI_CONF + phy->band_idx]);
+ path = FIELD_GET(MT_EE_WIFI_CONF0_TX_PATH,
+ eeprom[MT_EE_WIFI_CONF + band]);
}
- if (!nss || nss > 4)
- nss = 4;
+ if (!path || path > 4)
+ path = 4;
/* read tx/rx stream */
- nss_band = nss;
-
+ nss = path;
if (dev->dbdc_support) {
if (is_mt7915(&dev->mt76)) {
- nss_band = FIELD_GET(MT_EE_WIFI_CONF3_TX_PATH_B0,
- eeprom[MT_EE_WIFI_CONF + 3]);
- if (phy->band_idx)
- nss_band = FIELD_GET(MT_EE_WIFI_CONF3_TX_PATH_B1,
- eeprom[MT_EE_WIFI_CONF + 3]);
+ path = min_t(u8, path, 2);
+ nss = FIELD_GET(MT_EE_WIFI_CONF3_TX_PATH_B0,
+ eeprom[MT_EE_WIFI_CONF + 3]);
+ if (band)
+ nss = FIELD_GET(MT_EE_WIFI_CONF3_TX_PATH_B1,
+ eeprom[MT_EE_WIFI_CONF + 3]);
} else {
- nss_band = FIELD_GET(MT_EE_WIFI_CONF_STREAM_NUM,
- eeprom[MT_EE_WIFI_CONF + 2 + phy->band_idx]);
+ nss = FIELD_GET(MT_EE_WIFI_CONF_STREAM_NUM,
+ eeprom[MT_EE_WIFI_CONF + 2 + band]);
}
- nss_band_max = is_mt7986(&dev->mt76) ?
- MT_EE_NSS_MAX_DBDC_MA7986 : MT_EE_NSS_MAX_DBDC_MA7915;
- } else {
- nss_band_max = is_mt7986(&dev->mt76) ?
- MT_EE_NSS_MAX_MA7986 : MT_EE_NSS_MAX_MA7915;
+ if (!is_mt7986(&dev->mt76))
+ nss_max = 2;
}
- if (!nss_band || nss_band > nss_band_max)
- nss_band = nss_band_max;
-
- if (nss_band > nss) {
- dev_warn(dev->mt76.dev,
- "nss mismatch, nss(%d) nss_band(%d) band(%d) ext_phy(%d)\n",
- nss, nss_band, phy->band_idx, ext_phy);
- nss = nss_band;
- }
+ if (!nss)
+ nss = nss_max;
+ nss = min_t(u8, min_t(u8, nss_max, nss), path);
- mphy->chainmask = BIT(nss) - 1;
- if (ext_phy)
+ mphy->chainmask = BIT(path) - 1;
+ if (band)
mphy->chainmask <<= dev->chainshift;
- mphy->antenna_mask = BIT(nss_band) - 1;
+ mphy->antenna_mask = BIT(nss) - 1;
dev->chainmask |= mphy->chainmask;
dev->chainshift = hweight8(dev->mphy.chainmask);
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.h b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.h
index 7578ac6d0be6..f3e56817d36e 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.h
@@ -58,11 +58,6 @@ enum mt7915_eeprom_field {
#define MT_EE_RATE_DELTA_SIGN BIT(6)
#define MT_EE_RATE_DELTA_EN BIT(7)
-#define MT_EE_NSS_MAX_MA7915 4
-#define MT_EE_NSS_MAX_DBDC_MA7915 2
-#define MT_EE_NSS_MAX_MA7986 4
-#define MT_EE_NSS_MAX_DBDC_MA7986 4
-
enum mt7915_adie_sku {
MT7976_ONE_ADIE_DBDC = 0x7,
MT7975_ONE_ADIE = 0x8,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/init.c b/drivers/net/wireless/mediatek/mt76/mt7915/init.c
index cc2aac86bcfb..c810c31fbd6e 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/init.c
@@ -8,6 +8,7 @@
#include "mt7915.h"
#include "mac.h"
#include "mcu.h"
+#include "coredump.h"
#include "eeprom.h"
static const struct ieee80211_iface_limit if_limits[] = {
@@ -262,9 +263,8 @@ static void mt7915_led_set_brightness(struct led_classdev *led_cdev,
mt7915_led_set_config(led_cdev, 0xff, 0);
}
-static void
-mt7915_init_txpower(struct mt7915_dev *dev,
- struct ieee80211_supported_band *sband)
+void mt7915_init_txpower(struct mt7915_dev *dev,
+ struct ieee80211_supported_band *sband)
{
int i, n_chains = hweight8(dev->mphy.antenna_mask);
int nss_delta = mt76_tx_power_nss_delta(n_chains);
@@ -353,6 +353,10 @@ mt7915_init_wiphy(struct ieee80211_hw *hw)
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_HE);
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_UNSOL_BCAST_PROBE_RESP);
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_FILS_DISCOVERY);
+ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT);
+
+ 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,
@@ -444,9 +448,32 @@ mt7915_mac_init_band(struct mt7915_dev *dev, u8 band)
/* mt7915: disable rx rate report by default due to hw issues */
mt76_clear(dev, MT_DMA_DCR0(band), MT_DMA_DCR0_RXD_G5_EN);
+
+ /* clear estimated value of EIFS for Rx duration & OBSS time */
+ mt76_wr(dev, MT_WF_RMAC_RSVD0(band), MT_WF_RMAC_RSVD0_EIFS_CLR);
+
+ /* clear backoff time for Rx duration */
+ mt76_clear(dev, MT_WF_RMAC_MIB_AIRTIME1(band),
+ MT_WF_RMAC_MIB_NONQOSD_BACKOFF);
+ mt76_clear(dev, MT_WF_RMAC_MIB_AIRTIME3(band),
+ MT_WF_RMAC_MIB_QOS01_BACKOFF);
+ mt76_clear(dev, MT_WF_RMAC_MIB_AIRTIME4(band),
+ MT_WF_RMAC_MIB_QOS23_BACKOFF);
+
+ /* clear backoff time and set software compensation for OBSS time */
+ mask = MT_WF_RMAC_MIB_OBSS_BACKOFF | MT_WF_RMAC_MIB_ED_OFFSET;
+ set = FIELD_PREP(MT_WF_RMAC_MIB_OBSS_BACKOFF, 0) |
+ FIELD_PREP(MT_WF_RMAC_MIB_ED_OFFSET, 4);
+ mt76_rmw(dev, MT_WF_RMAC_MIB_AIRTIME0(band), mask, set);
+
+ /* filter out non-resp frames and get instanstaeous signal reporting */
+ mask = MT_WTBLOFF_TOP_RSCR_RCPI_MODE | MT_WTBLOFF_TOP_RSCR_RCPI_PARAM;
+ set = FIELD_PREP(MT_WTBLOFF_TOP_RSCR_RCPI_MODE, 0) |
+ FIELD_PREP(MT_WTBLOFF_TOP_RSCR_RCPI_PARAM, 0x3);
+ mt76_rmw(dev, MT_WTBLOFF_TOP_RSCR(band), mask, set);
}
-static void mt7915_mac_init(struct mt7915_dev *dev)
+void mt7915_mac_init(struct mt7915_dev *dev)
{
int i;
u32 rx_len = is_mt7915(&dev->mt76) ? 0x400 : 0x680;
@@ -476,7 +503,7 @@ static void mt7915_mac_init(struct mt7915_dev *dev)
}
}
-static int mt7915_txbf_init(struct mt7915_dev *dev)
+int mt7915_txbf_init(struct mt7915_dev *dev)
{
int ret;
@@ -513,7 +540,7 @@ mt7915_alloc_ext_phy(struct mt7915_dev *dev)
phy->mt76 = mphy;
/* Bind main phy to band0 and ext_phy to band1 for dbdc case */
- phy->band_idx = 1;
+ phy->mt76->band_idx = 1;
return phy;
}
@@ -633,7 +660,7 @@ static bool mt7915_band_config(struct mt7915_dev *dev)
{
bool ret = true;
- dev->phy.band_idx = 0;
+ dev->phy.mt76->band_idx = 0;
if (is_mt7986(&dev->mt76)) {
u32 sku = mt7915_check_adie(dev, true);
@@ -644,7 +671,7 @@ static bool mt7915_band_config(struct mt7915_dev *dev)
* dbdc is disabled.
*/
if (sku == MT7975_ONE_ADIE || sku == MT7976_ONE_ADIE) {
- dev->phy.band_idx = 1;
+ dev->phy.mt76->band_idx = 1;
ret = false;
}
} else {
@@ -700,45 +727,49 @@ mt7915_init_hardware(struct mt7915_dev *dev, struct mt7915_phy *phy2)
void mt7915_set_stream_vht_txbf_caps(struct mt7915_phy *phy)
{
- int nss;
+ int sts;
u32 *cap;
if (!phy->mt76->cap.has_5ghz)
return;
- nss = hweight8(phy->mt76->chainmask);
+ sts = hweight8(phy->mt76->chainmask);
cap = &phy->mt76->sband_5g.sband.vht_cap.cap;
*cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE |
- (3 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT);
+ FIELD_PREP(IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK,
+ sts - 1);
*cap &= ~(IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK |
IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE |
IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE);
- if (nss < 2)
+ if (sts < 2)
return;
*cap |= IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE |
IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE |
FIELD_PREP(IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK,
- nss - 1);
+ sts - 1);
}
static void
-mt7915_set_stream_he_txbf_caps(struct mt7915_dev *dev,
- struct ieee80211_sta_he_cap *he_cap,
- int vif, int nss)
+mt7915_set_stream_he_txbf_caps(struct mt7915_phy *phy,
+ struct ieee80211_sta_he_cap *he_cap, int vif)
{
+ struct mt7915_dev *dev = phy->dev;
struct ieee80211_he_cap_elem *elem = &he_cap->he_cap_elem;
- u8 c, nss_160;
+ int sts = hweight8(phy->mt76->chainmask);
+ u8 c, sts_160 = sts;
- /* Can do 1/2 of NSS streams in 160Mhz mode for mt7915 */
- if (is_mt7915(&dev->mt76) && !dev->dbdc_support)
- nss_160 = nss / 2;
- else
- nss_160 = nss;
+ /* Can do 1/2 of STS in 160Mhz mode for mt7915 */
+ if (is_mt7915(&dev->mt76)) {
+ if (!dev->dbdc_support)
+ sts_160 /= 2;
+ else
+ sts_160 = 0;
+ }
#ifdef CONFIG_MAC80211_MESH
if (vif == NL80211_IFTYPE_MESH_POINT)
@@ -748,8 +779,9 @@ mt7915_set_stream_he_txbf_caps(struct mt7915_dev *dev,
elem->phy_cap_info[3] &= ~IEEE80211_HE_PHY_CAP3_SU_BEAMFORMER;
elem->phy_cap_info[4] &= ~IEEE80211_HE_PHY_CAP4_MU_BEAMFORMER;
- c = IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK |
- IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_MASK;
+ c = IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK;
+ if (sts_160)
+ c |= IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_MASK;
elem->phy_cap_info[5] &= ~c;
c = IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMING_FB |
@@ -765,8 +797,9 @@ mt7915_set_stream_he_txbf_caps(struct mt7915_dev *dev,
elem->phy_cap_info[2] |= c;
c = IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE |
- IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_4 |
- IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_4;
+ IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_4;
+ if (sts_160)
+ c |= IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_4;
elem->phy_cap_info[4] |= c;
/* do not support NG16 due to spec D4.0 changes subcarrier idx */
@@ -778,11 +811,11 @@ mt7915_set_stream_he_txbf_caps(struct mt7915_dev *dev,
elem->phy_cap_info[6] |= c;
- if (nss < 2)
+ if (sts < 2)
return;
/* the maximum cap is 4 x 3, (Nr, Nc) = (3, 2) */
- elem->phy_cap_info[7] |= min_t(int, nss - 1, 2) << 3;
+ elem->phy_cap_info[7] |= min_t(int, sts - 1, 2) << 3;
if (vif != NL80211_IFTYPE_AP)
return;
@@ -791,12 +824,13 @@ mt7915_set_stream_he_txbf_caps(struct mt7915_dev *dev,
elem->phy_cap_info[4] |= IEEE80211_HE_PHY_CAP4_MU_BEAMFORMER;
/* num_snd_dim
- * for mt7915, max supported nss is 2 for bw > 80MHz
+ * for mt7915, max supported sts is 2 for bw > 80MHz and 0 if dbdc
*/
c = FIELD_PREP(IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK,
- nss - 1) |
- FIELD_PREP(IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_MASK,
- nss_160 - 1);
+ sts - 1);
+ if (sts_160)
+ c |= FIELD_PREP(IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_MASK,
+ sts_160 - 1);
elem->phy_cap_info[5] |= c;
c = IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMING_FB |
@@ -836,16 +870,19 @@ mt7915_init_he_caps(struct mt7915_phy *phy, enum nl80211_band band,
struct ieee80211_sband_iftype_data *data)
{
struct mt7915_dev *dev = phy->dev;
- int i, idx = 0, nss = hweight8(phy->mt76->chainmask);
+ int i, idx = 0, nss = hweight8(phy->mt76->antenna_mask);
u16 mcs_map = 0;
u16 mcs_map_160 = 0;
u8 nss_160;
- /* Can do 1/2 of NSS streams in 160Mhz mode for mt7915 */
- if (is_mt7915(&dev->mt76) && !dev->dbdc_support)
+ if (!is_mt7915(&dev->mt76))
+ nss_160 = nss;
+ else if (!dev->dbdc_support)
+ /* Can do 1/2 of NSS streams in 160Mhz mode for mt7915 */
nss_160 = nss / 2;
else
- nss_160 = nss;
+ /* Can't do 160MHz with mt7915 dbdc */
+ nss_160 = 0;
for (i = 0; i < 8; i++) {
if (i < nss)
@@ -891,11 +928,14 @@ mt7915_init_he_caps(struct mt7915_phy *phy, enum nl80211_band band,
if (band == NL80211_BAND_2GHZ)
he_cap_elem->phy_cap_info[0] =
IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G;
- else
+ else if (nss_160)
he_cap_elem->phy_cap_info[0] =
IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G |
IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G;
+ else
+ he_cap_elem->phy_cap_info[0] =
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G;
he_cap_elem->phy_cap_info[1] =
IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD;
@@ -949,9 +989,11 @@ mt7915_init_he_caps(struct mt7915_phy *phy, enum nl80211_band band,
IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF_AND_08_US_GI;
he_cap_elem->phy_cap_info[8] |=
IEEE80211_HE_PHY_CAP8_20MHZ_IN_40MHZ_HE_PPDU_IN_2G |
- IEEE80211_HE_PHY_CAP8_20MHZ_IN_160MHZ_HE_PPDU |
- IEEE80211_HE_PHY_CAP8_80MHZ_IN_160MHZ_HE_PPDU |
IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_484;
+ if (nss_160)
+ he_cap_elem->phy_cap_info[8] |=
+ IEEE80211_HE_PHY_CAP8_20MHZ_IN_160MHZ_HE_PPDU |
+ IEEE80211_HE_PHY_CAP8_80MHZ_IN_160MHZ_HE_PPDU;
he_cap_elem->phy_cap_info[9] |=
IEEE80211_HE_PHY_CAP9_LONGER_THAN_16_SIGB_OFDM_SYM |
IEEE80211_HE_PHY_CAP9_NON_TRIGGERED_CQI_FEEDBACK |
@@ -969,7 +1011,7 @@ mt7915_init_he_caps(struct mt7915_phy *phy, enum nl80211_band band,
he_mcs->rx_mcs_80p80 = cpu_to_le16(mcs_map_160);
he_mcs->tx_mcs_80p80 = cpu_to_le16(mcs_map_160);
- mt7915_set_stream_he_txbf_caps(dev, he_cap, i, nss);
+ mt7915_set_stream_he_txbf_caps(phy, he_cap, i);
memset(he_cap->ppe_thres, 0, sizeof(he_cap->ppe_thres));
if (he_cap_elem->phy_cap_info[6] &
@@ -1078,6 +1120,8 @@ int mt7915_register_device(struct mt7915_dev *dev)
init_waitqueue_head(&dev->reset_wait);
INIT_WORK(&dev->reset_work, mt7915_mac_reset_work);
+ INIT_WORK(&dev->dump_work, mt7915_mac_dump_work);
+ mutex_init(&dev->dump_mutex);
dev->dbdc_support = mt7915_band_config(dev);
@@ -1118,7 +1162,15 @@ int mt7915_register_device(struct mt7915_dev *dev)
goto unreg_thermal;
}
- mt7915_init_debugfs(&dev->phy);
+ dev->recovery.hw_init_done = true;
+
+ ret = mt7915_init_debugfs(&dev->phy);
+ if (ret)
+ goto unreg_thermal;
+
+ ret = mt7915_coredump_register(dev);
+ if (ret)
+ goto unreg_thermal;
return 0;
@@ -1137,6 +1189,7 @@ free_phy2:
void mt7915_unregister_device(struct mt7915_dev *dev)
{
mt7915_unregister_ext_phy(dev);
+ mt7915_coredump_unregister(dev);
mt7915_unregister_thermal(&dev->phy);
mt76_unregister_device(&dev->mt76);
mt7915_stop_hardware(dev);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
index a4bcc617c1a3..f0d5a3603902 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
@@ -3,12 +3,13 @@
#include <linux/etherdevice.h>
#include <linux/timekeeping.h>
+#include "coredump.h"
#include "mt7915.h"
#include "../dma.h"
#include "mac.h"
#include "mcu.h"
-#define to_rssi(field, rxv) ((FIELD_GET(field, rxv) - 220) / 2)
+#define to_rssi(field, rcpi) ((FIELD_GET(field, rcpi) - 220) / 2)
static const struct mt7915_dfs_radar_spec etsi_radar_specs = {
.pulse_th = { 110, -10, -80, 40, 5200, 128, 5200 },
@@ -118,6 +119,7 @@ static void mt7915_mac_sta_poll(struct mt7915_dev *dev)
bool clear = false;
u32 addr, val;
u16 idx;
+ s8 rssi[4];
u8 bw;
spin_lock_bh(&dev->sta_poll_lock);
@@ -131,6 +133,8 @@ static void mt7915_mac_sta_poll(struct mt7915_dev *dev)
spin_unlock_bh(&dev->sta_poll_lock);
idx = msta->wcid.idx;
+
+ /* refresh peer's airtime reporting */
addr = mt7915_mac_wtbl_lmac_addr(dev, idx, 20);
for (i = 0; i < IEEE80211_NUM_ACS; i++) {
@@ -161,9 +165,9 @@ static void mt7915_mac_sta_poll(struct mt7915_dev *dev)
sta = container_of((void *)msta, struct ieee80211_sta,
drv_priv);
for (i = 0; i < IEEE80211_NUM_ACS; i++) {
- u8 q = mt76_connac_lmac_mapping(i);
- u32 tx_cur = tx_time[q];
- u32 rx_cur = rx_time[q];
+ u8 queue = mt76_connac_lmac_mapping(i);
+ u32 tx_cur = tx_time[queue];
+ u32 rx_cur = rx_time[queue];
u8 tid = ac_to_tid[i];
if (!tx_cur && !rx_cur)
@@ -209,13 +213,69 @@ static void mt7915_mac_sta_poll(struct mt7915_dev *dev)
else
rate->flags &= ~RATE_INFO_FLAGS_SHORT_GI;
}
+
+ /* get signal strength of resp frames (CTS/BA/ACK) */
+ addr = mt7915_mac_wtbl_lmac_addr(dev, idx, 30);
+ val = mt76_rr(dev, addr);
+
+ rssi[0] = to_rssi(GENMASK(7, 0), val);
+ rssi[1] = to_rssi(GENMASK(15, 8), val);
+ rssi[2] = to_rssi(GENMASK(23, 16), val);
+ rssi[3] = to_rssi(GENMASK(31, 14), val);
+
+ msta->ack_signal =
+ mt76_rx_signal(msta->vif->phy->mt76->antenna_mask, rssi);
+
+ ewma_avg_signal_add(&msta->avg_ack_signal, -msta->ack_signal);
}
rcu_read_unlock();
}
+void mt7915_mac_enable_rtscts(struct mt7915_dev *dev,
+ struct ieee80211_vif *vif, bool enable)
+{
+ struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv;
+ u32 addr;
+
+ addr = mt7915_mac_wtbl_lmac_addr(dev, mvif->sta.wcid.idx, 5);
+ if (enable)
+ mt76_set(dev, addr, BIT(5));
+ else
+ mt76_clear(dev, addr, BIT(5));
+}
+
+static void
+mt7915_wed_check_ppe(struct mt7915_dev *dev, struct mt76_queue *q,
+ struct mt7915_sta *msta, struct sk_buff *skb,
+ u32 info)
+{
+ struct ieee80211_vif *vif;
+ struct wireless_dev *wdev;
+
+ if (!msta || !msta->vif)
+ return;
+
+ if (!(q->flags & MT_QFLAG_WED) ||
+ FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) != MT76_WED_Q_RX)
+ return;
+
+ if (!(info & MT_DMA_INFO_PPE_VLD))
+ return;
+
+ vif = container_of((void *)msta->vif, struct ieee80211_vif,
+ drv_priv);
+ wdev = ieee80211_vif_to_wdev(vif);
+ skb->dev = wdev->netdev;
+
+ mtk_wed_device_ppe_check(&dev->mt76.mmio.wed, skb,
+ FIELD_GET(MT_DMA_PPE_CPU_REASON, info),
+ FIELD_GET(MT_DMA_PPE_ENTRY, info));
+}
+
static int
-mt7915_mac_fill_rx(struct mt7915_dev *dev, struct sk_buff *skb)
+mt7915_mac_fill_rx(struct mt7915_dev *dev, struct sk_buff *skb,
+ enum mt76_rxq_id q, u32 *info)
{
struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb;
struct mt76_phy *mphy = &dev->mt76.phy;
@@ -242,7 +302,7 @@ mt7915_mac_fill_rx(struct mt7915_dev *dev, struct sk_buff *skb)
memset(status, 0, sizeof(*status));
- if ((rxd1 & MT_RXD1_NORMAL_BAND_IDX) && !phy->band_idx) {
+ if ((rxd1 & MT_RXD1_NORMAL_BAND_IDX) && !phy->mt76->band_idx) {
mphy = dev->mt76.phys[MT_BAND1];
if (!mphy)
return -EINVAL;
@@ -482,6 +542,8 @@ mt7915_mac_fill_rx(struct mt7915_dev *dev, struct sk_buff *skb)
}
} else {
status->flag |= RX_FLAG_8023;
+ mt7915_wed_check_ppe(dev, &dev->mt76.q_rx[q], msta, skb,
+ *info);
}
if (rxv && mode >= MT_PHY_TYPE_HE_SU && !(status->flag & RX_FLAG_8023))
@@ -513,7 +575,7 @@ mt7915_mac_fill_rx_vector(struct mt7915_dev *dev, struct sk_buff *skb)
int i;
band_idx = le32_get_bits(rxv_hdr[1], MT_RXV_HDR_BAND_IDX);
- if (band_idx && !phy->band_idx) {
+ if (band_idx && !phy->mt76->band_idx) {
phy = mt7915_ext_phy(dev);
if (!phy)
goto out;
@@ -905,17 +967,19 @@ mt7915_mac_tx_free(struct mt7915_dev *dev, void *data, int len)
total = le16_get_bits(free->ctrl, MT_TX_FREE_MSDU_CNT);
v3 = (FIELD_GET(MT_TX_FREE_VER, txd) == 0x4);
- if (WARN_ON_ONCE((void *)&tx_info[total >> v3] > end))
- return;
for (cur_info = tx_info; count < total; cur_info++) {
- u32 msdu, info = le32_to_cpu(*cur_info);
+ u32 msdu, info;
u8 i;
+ if (WARN_ON_ONCE((void *)cur_info >= end))
+ return;
+
/*
* 1'b1: new wcid pair.
* 1'b0: msdu_id with the same 'wcid pair' as above.
*/
+ info = le32_to_cpu(*cur_info);
if (info & MT_TX_FREE_PAIR) {
struct mt7915_sta *msta;
struct mt76_wcid *wcid;
@@ -1063,7 +1127,7 @@ bool mt7915_rx_check(struct mt76_dev *mdev, void *data, int len)
}
void mt7915_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
- struct sk_buff *skb)
+ struct sk_buff *skb, u32 *info)
{
struct mt7915_dev *dev = container_of(mdev, struct mt7915_dev, mt76);
__le32 *rxd = (__le32 *)skb->data;
@@ -1097,7 +1161,7 @@ void mt7915_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
dev_kfree_skb(skb);
break;
case PKT_TYPE_NORMAL:
- if (!mt7915_mac_fill_rx(dev, skb)) {
+ if (!mt7915_mac_fill_rx(dev, skb, q, info)) {
mt76_rx(&dev->mt76, q, skb);
return;
}
@@ -1111,7 +1175,7 @@ void mt7915_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
void mt7915_mac_cca_stats_reset(struct mt7915_phy *phy)
{
struct mt7915_dev *dev = phy->dev;
- u32 reg = MT_WF_PHY_RX_CTRL1(phy->band_idx);
+ u32 reg = MT_WF_PHY_RX_CTRL1(phy->mt76->band_idx);
mt76_clear(dev, reg, MT_WF_PHY_RX_CTRL1_STSCNT_EN);
mt76_set(dev, reg, BIT(11) | BIT(9));
@@ -1123,19 +1187,15 @@ void mt7915_mac_reset_counters(struct mt7915_phy *phy)
int i;
for (i = 0; i < 4; i++) {
- mt76_rr(dev, MT_TX_AGG_CNT(phy->band_idx, i));
- mt76_rr(dev, MT_TX_AGG_CNT2(phy->band_idx, i));
+ mt76_rr(dev, MT_TX_AGG_CNT(phy->mt76->band_idx, i));
+ mt76_rr(dev, MT_TX_AGG_CNT2(phy->mt76->band_idx, i));
}
- i = 0;
phy->mt76->survey_time = ktime_get_boottime();
- if (phy->band_idx)
- i = ARRAY_SIZE(dev->mt76.aggr_stats) / 2;
-
- memset(&dev->mt76.aggr_stats[i], 0, sizeof(dev->mt76.aggr_stats) / 2);
+ memset(phy->mt76->aggr_stats, 0, sizeof(phy->mt76->aggr_stats));
/* reset airtime counters */
- mt76_set(dev, MT_WF_RMAC_MIB_AIRTIME0(phy->band_idx),
+ mt76_set(dev, MT_WF_RMAC_MIB_AIRTIME0(phy->mt76->band_idx),
MT_WF_RMAC_MIB_RXTIME_CLR);
mt7915_mcu_get_chan_mib_info(phy, true);
@@ -1151,7 +1211,8 @@ void mt7915_mac_set_timing(struct mt7915_phy *phy)
FIELD_PREP(MT_TIMEOUT_VAL_CCA, 48);
u32 ofdm = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, 60) |
FIELD_PREP(MT_TIMEOUT_VAL_CCA, 28);
- int offset;
+ u8 band = phy->mt76->band_idx;
+ int eifs_ofdm = 360, sifs = 10, offset;
bool a_band = !(phy->mt76->chandef.chan->band == NL80211_BAND_2GHZ);
if (!test_bit(MT76_STATE_RUNNING, &phy->mt76->state))
@@ -1161,7 +1222,7 @@ void mt7915_mac_set_timing(struct mt7915_phy *phy)
coverage_class = max_t(s16, dev->phy.coverage_class,
ext_phy->coverage_class);
- mt76_set(dev, MT_ARB_SCR(phy->band_idx),
+ mt76_set(dev, MT_ARB_SCR(band),
MT_ARB_SCR_TX_DISABLE | MT_ARB_SCR_RX_DISABLE);
udelay(1);
@@ -1169,39 +1230,48 @@ void mt7915_mac_set_timing(struct mt7915_phy *phy)
reg_offset = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, offset) |
FIELD_PREP(MT_TIMEOUT_VAL_CCA, offset);
- mt76_wr(dev, MT_TMAC_CDTR(phy->band_idx), cck + reg_offset);
- mt76_wr(dev, MT_TMAC_ODTR(phy->band_idx), ofdm + reg_offset);
- mt76_wr(dev, MT_TMAC_ICR0(phy->band_idx),
- FIELD_PREP(MT_IFS_EIFS_OFDM, a_band ? 84 : 78) |
+ if (!is_mt7915(&dev->mt76)) {
+ if (!a_band) {
+ mt76_wr(dev, MT_TMAC_ICR1(band),
+ FIELD_PREP(MT_IFS_EIFS_CCK, 314));
+ eifs_ofdm = 78;
+ } else {
+ eifs_ofdm = 84;
+ }
+ } else if (a_band) {
+ sifs = 16;
+ }
+
+ mt76_wr(dev, MT_TMAC_CDTR(band), cck + reg_offset);
+ mt76_wr(dev, MT_TMAC_ODTR(band), ofdm + reg_offset);
+ mt76_wr(dev, MT_TMAC_ICR0(band),
+ FIELD_PREP(MT_IFS_EIFS_OFDM, eifs_ofdm) |
FIELD_PREP(MT_IFS_RIFS, 2) |
- FIELD_PREP(MT_IFS_SIFS, 10) |
+ FIELD_PREP(MT_IFS_SIFS, sifs) |
FIELD_PREP(MT_IFS_SLOT, phy->slottime));
- mt76_wr(dev, MT_TMAC_ICR1(phy->band_idx),
- FIELD_PREP(MT_IFS_EIFS_CCK, 314));
-
if (phy->slottime < 20 || a_band)
val = MT7915_CFEND_RATE_DEFAULT;
else
val = MT7915_CFEND_RATE_11B;
- mt76_rmw_field(dev, MT_AGG_ACR0(phy->band_idx), MT_AGG_ACR_CFEND_RATE, val);
- mt76_clear(dev, MT_ARB_SCR(phy->band_idx),
+ mt76_rmw_field(dev, MT_AGG_ACR0(band), MT_AGG_ACR_CFEND_RATE, val);
+ mt76_clear(dev, MT_ARB_SCR(band),
MT_ARB_SCR_TX_DISABLE | MT_ARB_SCR_RX_DISABLE);
}
-void mt7915_mac_enable_nf(struct mt7915_dev *dev, bool ext_phy)
+void mt7915_mac_enable_nf(struct mt7915_dev *dev, bool band)
{
u32 reg;
- reg = is_mt7915(&dev->mt76) ? MT_WF_PHY_RXTD12(ext_phy) :
- MT_WF_PHY_RXTD12_MT7916(ext_phy);
+ reg = is_mt7915(&dev->mt76) ? MT_WF_PHY_RXTD12(band) :
+ MT_WF_PHY_RXTD12_MT7916(band);
mt76_set(dev, reg,
MT_WF_PHY_RXTD12_IRPI_SW_CLR_ONLY |
MT_WF_PHY_RXTD12_IRPI_SW_CLR);
- reg = is_mt7915(&dev->mt76) ? MT_WF_PHY_RX_CTRL1(ext_phy) :
- MT_WF_PHY_RX_CTRL1_MT7916(ext_phy);
+ reg = is_mt7915(&dev->mt76) ? MT_WF_PHY_RX_CTRL1(band) :
+ MT_WF_PHY_RX_CTRL1_MT7916(band);
mt76_set(dev, reg, FIELD_PREP(MT_WF_PHY_RX_CTRL1_IPI_EN, 0x5));
}
@@ -1239,7 +1309,7 @@ void mt7915_update_channel(struct mt76_phy *mphy)
mt7915_mcu_get_chan_mib_info(phy, false);
- nf = mt7915_phy_get_nf(phy, phy->band_idx);
+ nf = mt7915_phy_get_nf(phy, phy->mt76->band_idx);
if (!phy->noise)
phy->noise = nf << 4;
else if (nf)
@@ -1254,7 +1324,7 @@ mt7915_wait_reset_state(struct mt7915_dev *dev, u32 state)
bool ret;
ret = wait_event_timeout(dev->reset_wait,
- (READ_ONCE(dev->reset_state) & state),
+ (READ_ONCE(dev->recovery.state) & state),
MT7915_RESET_TIMEOUT);
WARN(!ret, "Timeout waiting for MCU reset state %x\n", state);
@@ -1295,85 +1365,180 @@ mt7915_update_beacons(struct mt7915_dev *dev)
mt7915_update_vif_beacon, mphy_ext->hw);
}
-static void
-mt7915_dma_reset(struct mt7915_dev *dev)
+void mt7915_tx_token_put(struct mt7915_dev *dev)
{
- struct mt76_phy *mphy_ext = dev->mt76.phys[MT_BAND1];
- u32 hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0);
- int i;
+ struct mt76_txwi_cache *txwi;
+ int id;
- mt76_clear(dev, MT_WFDMA0_GLO_CFG,
- MT_WFDMA0_GLO_CFG_TX_DMA_EN |
- MT_WFDMA0_GLO_CFG_RX_DMA_EN);
+ spin_lock_bh(&dev->mt76.token_lock);
+ idr_for_each_entry(&dev->mt76.token, txwi, id) {
+ mt7915_txwi_free(dev, txwi, NULL, NULL);
+ dev->mt76.token_count--;
+ }
+ spin_unlock_bh(&dev->mt76.token_lock);
+ idr_destroy(&dev->mt76.token);
+}
+
+static int
+mt7915_mac_restart(struct mt7915_dev *dev)
+{
+ struct mt7915_phy *phy2;
+ struct mt76_phy *ext_phy;
+ struct mt76_dev *mdev = &dev->mt76;
+ int i, ret;
+
+ ext_phy = dev->mt76.phys[MT_BAND1];
+ phy2 = ext_phy ? ext_phy->priv : NULL;
- if (is_mt7915(&dev->mt76))
- mt76_clear(dev, MT_WFDMA1_GLO_CFG,
- MT_WFDMA1_GLO_CFG_TX_DMA_EN |
- MT_WFDMA1_GLO_CFG_RX_DMA_EN);
if (dev->hif2) {
- mt76_clear(dev, MT_WFDMA0_GLO_CFG + hif1_ofs,
- MT_WFDMA0_GLO_CFG_TX_DMA_EN |
- MT_WFDMA0_GLO_CFG_RX_DMA_EN);
+ mt76_wr(dev, MT_INT1_MASK_CSR, 0x0);
+ mt76_wr(dev, MT_INT1_SOURCE_CSR, ~0);
+ }
- if (is_mt7915(&dev->mt76))
- mt76_clear(dev, MT_WFDMA1_GLO_CFG + hif1_ofs,
- MT_WFDMA1_GLO_CFG_TX_DMA_EN |
- MT_WFDMA1_GLO_CFG_RX_DMA_EN);
+ if (dev_is_pci(mdev->dev)) {
+ mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0x0);
+ if (dev->hif2)
+ mt76_wr(dev, MT_PCIE1_MAC_INT_ENABLE, 0x0);
+ }
+
+ set_bit(MT76_RESET, &dev->mphy.state);
+ set_bit(MT76_MCU_RESET, &dev->mphy.state);
+ wake_up(&dev->mt76.mcu.wait);
+ if (ext_phy) {
+ set_bit(MT76_RESET, &ext_phy->state);
+ set_bit(MT76_MCU_RESET, &ext_phy->state);
}
- usleep_range(1000, 2000);
+ /* lock/unlock all queues to ensure that no tx is pending */
+ mt76_txq_schedule_all(&dev->mphy);
+ if (ext_phy)
+ mt76_txq_schedule_all(ext_phy);
- for (i = 0; i < __MT_TXQ_MAX; i++) {
- mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[i], true);
- if (mphy_ext)
- mt76_queue_tx_cleanup(dev, mphy_ext->q_tx[i], true);
+ /* disable all tx/rx napi */
+ mt76_worker_disable(&dev->mt76.tx_worker);
+ mt76_for_each_q_rx(mdev, i) {
+ if (mdev->q_rx[i].ndesc)
+ napi_disable(&dev->mt76.napi[i]);
}
+ napi_disable(&dev->mt76.tx_napi);
- for (i = 0; i < __MT_MCUQ_MAX; i++)
- mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[i], true);
+ /* token reinit */
+ mt7915_tx_token_put(dev);
+ idr_init(&dev->mt76.token);
- mt76_for_each_q_rx(&dev->mt76, i)
- mt76_queue_rx_reset(dev, i);
+ mt7915_dma_reset(dev, true);
- mt76_tx_status_check(&dev->mt76, true);
+ local_bh_disable();
+ mt76_for_each_q_rx(mdev, i) {
+ if (mdev->q_rx[i].ndesc) {
+ napi_enable(&dev->mt76.napi[i]);
+ napi_schedule(&dev->mt76.napi[i]);
+ }
+ }
+ local_bh_enable();
+ clear_bit(MT76_MCU_RESET, &dev->mphy.state);
+ clear_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state);
- /* re-init prefetch settings after reset */
- mt7915_dma_prefetch(dev);
+ mt76_wr(dev, MT_INT_MASK_CSR, dev->mt76.mmio.irqmask);
+ mt76_wr(dev, MT_INT_SOURCE_CSR, ~0);
- mt76_set(dev, MT_WFDMA0_GLO_CFG,
- MT_WFDMA0_GLO_CFG_TX_DMA_EN | MT_WFDMA0_GLO_CFG_RX_DMA_EN);
- if (is_mt7915(&dev->mt76))
- mt76_set(dev, MT_WFDMA1_GLO_CFG,
- MT_WFDMA1_GLO_CFG_TX_DMA_EN |
- MT_WFDMA1_GLO_CFG_RX_DMA_EN |
- MT_WFDMA1_GLO_CFG_OMIT_TX_INFO |
- MT_WFDMA1_GLO_CFG_OMIT_RX_INFO);
if (dev->hif2) {
- mt76_set(dev, MT_WFDMA0_GLO_CFG + hif1_ofs,
- MT_WFDMA0_GLO_CFG_TX_DMA_EN |
- MT_WFDMA0_GLO_CFG_RX_DMA_EN);
+ mt76_wr(dev, MT_INT1_MASK_CSR, dev->mt76.mmio.irqmask);
+ mt76_wr(dev, MT_INT1_SOURCE_CSR, ~0);
+ }
+ if (dev_is_pci(mdev->dev)) {
+ mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff);
+ if (dev->hif2)
+ mt76_wr(dev, MT_PCIE1_MAC_INT_ENABLE, 0xff);
+ }
+
+ /* load firmware */
+ ret = mt7915_mcu_init_firmware(dev);
+ if (ret)
+ goto out;
+
+ /* set the necessary init items */
+ ret = mt7915_mcu_set_eeprom(dev);
+ if (ret)
+ goto out;
+
+ mt7915_mac_init(dev);
+ mt7915_init_txpower(dev, &dev->mphy.sband_2g.sband);
+ mt7915_init_txpower(dev, &dev->mphy.sband_5g.sband);
+ ret = mt7915_txbf_init(dev);
- if (is_mt7915(&dev->mt76))
- mt76_set(dev, MT_WFDMA1_GLO_CFG + hif1_ofs,
- MT_WFDMA1_GLO_CFG_TX_DMA_EN |
- MT_WFDMA1_GLO_CFG_RX_DMA_EN |
- MT_WFDMA1_GLO_CFG_OMIT_TX_INFO |
- MT_WFDMA1_GLO_CFG_OMIT_RX_INFO);
+ if (test_bit(MT76_STATE_RUNNING, &dev->mphy.state)) {
+ ret = mt7915_run(dev->mphy.hw);
+ if (ret)
+ goto out;
+ }
+
+ if (ext_phy && test_bit(MT76_STATE_RUNNING, &ext_phy->state)) {
+ ret = mt7915_run(ext_phy->hw);
+ if (ret)
+ goto out;
}
+
+out:
+ /* reset done */
+ clear_bit(MT76_RESET, &dev->mphy.state);
+ if (phy2)
+ clear_bit(MT76_RESET, &phy2->mt76->state);
+
+ local_bh_disable();
+ napi_enable(&dev->mt76.tx_napi);
+ napi_schedule(&dev->mt76.tx_napi);
+ local_bh_enable();
+
+ mt76_worker_enable(&dev->mt76.tx_worker);
+
+ return ret;
}
-void mt7915_tx_token_put(struct mt7915_dev *dev)
+static void
+mt7915_mac_full_reset(struct mt7915_dev *dev)
{
- struct mt76_txwi_cache *txwi;
- int id;
+ struct mt76_phy *ext_phy;
+ int i;
- spin_lock_bh(&dev->mt76.token_lock);
- idr_for_each_entry(&dev->mt76.token, txwi, id) {
- mt7915_txwi_free(dev, txwi, NULL, NULL);
- dev->mt76.token_count--;
+ ext_phy = dev->mt76.phys[MT_BAND1];
+
+ dev->recovery.hw_full_reset = true;
+
+ wake_up(&dev->mt76.mcu.wait);
+ ieee80211_stop_queues(mt76_hw(dev));
+ if (ext_phy)
+ ieee80211_stop_queues(ext_phy->hw);
+
+ cancel_delayed_work_sync(&dev->mphy.mac_work);
+ if (ext_phy)
+ cancel_delayed_work_sync(&ext_phy->mac_work);
+
+ mutex_lock(&dev->mt76.mutex);
+ for (i = 0; i < 10; i++) {
+ if (!mt7915_mac_restart(dev))
+ break;
}
- spin_unlock_bh(&dev->mt76.token_lock);
- idr_destroy(&dev->mt76.token);
+ mutex_unlock(&dev->mt76.mutex);
+
+ if (i == 10)
+ dev_err(dev->mt76.dev, "chip full reset failed\n");
+
+ ieee80211_restart_hw(mt76_hw(dev));
+ if (ext_phy)
+ ieee80211_restart_hw(ext_phy->hw);
+
+ ieee80211_wake_queues(mt76_hw(dev));
+ if (ext_phy)
+ ieee80211_wake_queues(ext_phy->hw);
+
+ dev->recovery.hw_full_reset = false;
+ ieee80211_queue_delayed_work(mt76_hw(dev), &dev->mphy.mac_work,
+ MT7915_WATCHDOG_TIME);
+ if (ext_phy)
+ ieee80211_queue_delayed_work(ext_phy->hw,
+ &ext_phy->mac_work,
+ MT7915_WATCHDOG_TIME);
}
/* system error recovery */
@@ -1388,7 +1553,33 @@ void mt7915_mac_reset_work(struct work_struct *work)
ext_phy = dev->mt76.phys[MT_BAND1];
phy2 = ext_phy ? ext_phy->priv : NULL;
- if (!(READ_ONCE(dev->reset_state) & MT_MCU_CMD_STOP_DMA))
+ /* chip full reset */
+ if (dev->recovery.restart) {
+ /* disable WA/WM WDT */
+ mt76_clear(dev, MT_WFDMA0_MCU_HOST_INT_ENA,
+ MT_MCU_CMD_WDT_MASK);
+
+ if (READ_ONCE(dev->recovery.state) & MT_MCU_CMD_WA_WDT)
+ dev->recovery.wa_reset_count++;
+ else
+ dev->recovery.wm_reset_count++;
+
+ mt7915_mac_full_reset(dev);
+
+ /* enable mcu irq */
+ mt7915_irq_enable(dev, MT_INT_MCU_CMD);
+ mt7915_irq_disable(dev, 0);
+
+ /* enable WA/WM WDT */
+ mt76_set(dev, MT_WFDMA0_MCU_HOST_INT_ENA, MT_MCU_CMD_WDT_MASK);
+
+ dev->recovery.state = MT_MCU_CMD_NORMAL_STATE;
+ dev->recovery.restart = false;
+ return;
+ }
+
+ /* chip partial reset */
+ if (!(READ_ONCE(dev->recovery.state) & MT_MCU_CMD_STOP_DMA))
return;
ieee80211_stop_queues(mt76_hw(dev));
@@ -1413,7 +1604,7 @@ void mt7915_mac_reset_work(struct work_struct *work)
mt76_wr(dev, MT_MCU_INT_EVENT, MT_MCU_INT_EVENT_DMA_STOPPED);
if (mt7915_wait_reset_state(dev, MT_MCU_CMD_RESET_DONE)) {
- mt7915_dma_reset(dev);
+ mt7915_dma_reset(dev, false);
mt7915_tx_token_put(dev);
idr_init(&dev->mt76.token);
@@ -1462,132 +1653,227 @@ void mt7915_mac_reset_work(struct work_struct *work)
MT7915_WATCHDOG_TIME);
}
+/* firmware coredump */
+void mt7915_mac_dump_work(struct work_struct *work)
+{
+ const struct mt7915_mem_region *mem_region;
+ struct mt7915_crash_data *crash_data;
+ struct mt7915_dev *dev;
+ struct mt7915_mem_hdr *hdr;
+ size_t buf_len;
+ int i;
+ u32 num;
+ u8 *buf;
+
+ dev = container_of(work, struct mt7915_dev, dump_work);
+
+ mutex_lock(&dev->dump_mutex);
+
+ crash_data = mt7915_coredump_new(dev);
+ if (!crash_data) {
+ mutex_unlock(&dev->dump_mutex);
+ goto skip_coredump;
+ }
+
+ mem_region = mt7915_coredump_get_mem_layout(dev, &num);
+ if (!mem_region || !crash_data->memdump_buf_len) {
+ mutex_unlock(&dev->dump_mutex);
+ goto skip_memdump;
+ }
+
+ buf = crash_data->memdump_buf;
+ buf_len = crash_data->memdump_buf_len;
+
+ /* dumping memory content... */
+ memset(buf, 0, buf_len);
+ for (i = 0; i < num; i++) {
+ if (mem_region->len > buf_len) {
+ dev_warn(dev->mt76.dev, "%s len %lu is too large\n",
+ mem_region->name,
+ (unsigned long)mem_region->len);
+ break;
+ }
+
+ /* reserve space for the header */
+ hdr = (void *)buf;
+ buf += sizeof(*hdr);
+ buf_len -= sizeof(*hdr);
+
+ mt7915_memcpy_fromio(dev, buf, mem_region->start,
+ mem_region->len);
+
+ hdr->start = mem_region->start;
+ hdr->len = mem_region->len;
+
+ if (!mem_region->len)
+ /* note: the header remains, just with zero length */
+ break;
+
+ buf += mem_region->len;
+ buf_len -= mem_region->len;
+
+ mem_region++;
+ }
+
+ mutex_unlock(&dev->dump_mutex);
+
+skip_memdump:
+ mt7915_coredump_submit(dev);
+skip_coredump:
+ queue_work(dev->mt76.wq, &dev->reset_work);
+}
+
+void mt7915_reset(struct mt7915_dev *dev)
+{
+ if (!dev->recovery.hw_init_done)
+ return;
+
+ if (dev->recovery.hw_full_reset)
+ return;
+
+ /* wm/wa exception: do full recovery */
+ if (READ_ONCE(dev->recovery.state) & MT_MCU_CMD_WDT_MASK) {
+ dev->recovery.restart = true;
+ dev_info(dev->mt76.dev,
+ "%s indicated firmware crash, attempting recovery\n",
+ wiphy_name(dev->mt76.hw->wiphy));
+
+ mt7915_irq_disable(dev, MT_INT_MCU_CMD);
+ queue_work(dev->mt76.wq, &dev->dump_work);
+ return;
+ }
+
+ queue_work(dev->mt76.wq, &dev->reset_work);
+ wake_up(&dev->reset_wait);
+}
+
void mt7915_mac_update_stats(struct mt7915_phy *phy)
{
struct mt7915_dev *dev = phy->dev;
struct mib_stats *mib = &phy->mib;
- int i, aggr0, aggr1, cnt;
+ int i, aggr0 = 0, aggr1, cnt;
+ u8 band = phy->mt76->band_idx;
u32 val;
- cnt = mt76_rr(dev, MT_MIB_SDR3(phy->band_idx));
+ cnt = mt76_rr(dev, MT_MIB_SDR3(band));
mib->fcs_err_cnt += is_mt7915(&dev->mt76) ?
FIELD_GET(MT_MIB_SDR3_FCS_ERR_MASK, cnt) :
FIELD_GET(MT_MIB_SDR3_FCS_ERR_MASK_MT7916, cnt);
- cnt = mt76_rr(dev, MT_MIB_SDR4(phy->band_idx));
+ cnt = mt76_rr(dev, MT_MIB_SDR4(band));
mib->rx_fifo_full_cnt += FIELD_GET(MT_MIB_SDR4_RX_FIFO_FULL_MASK, cnt);
- cnt = mt76_rr(dev, MT_MIB_SDR5(phy->band_idx));
+ cnt = mt76_rr(dev, MT_MIB_SDR5(band));
mib->rx_mpdu_cnt += cnt;
- cnt = mt76_rr(dev, MT_MIB_SDR6(phy->band_idx));
+ cnt = mt76_rr(dev, MT_MIB_SDR6(band));
mib->channel_idle_cnt += FIELD_GET(MT_MIB_SDR6_CHANNEL_IDL_CNT_MASK, cnt);
- cnt = mt76_rr(dev, MT_MIB_SDR7(phy->band_idx));
+ cnt = mt76_rr(dev, MT_MIB_SDR7(band));
mib->rx_vector_mismatch_cnt +=
FIELD_GET(MT_MIB_SDR7_RX_VECTOR_MISMATCH_CNT_MASK, cnt);
- cnt = mt76_rr(dev, MT_MIB_SDR8(phy->band_idx));
+ cnt = mt76_rr(dev, MT_MIB_SDR8(band));
mib->rx_delimiter_fail_cnt +=
FIELD_GET(MT_MIB_SDR8_RX_DELIMITER_FAIL_CNT_MASK, cnt);
- cnt = mt76_rr(dev, MT_MIB_SDR10(phy->band_idx));
+ cnt = mt76_rr(dev, MT_MIB_SDR10(band));
mib->rx_mrdy_cnt += is_mt7915(&dev->mt76) ?
FIELD_GET(MT_MIB_SDR10_MRDY_COUNT_MASK, cnt) :
FIELD_GET(MT_MIB_SDR10_MRDY_COUNT_MASK_MT7916, cnt);
- cnt = mt76_rr(dev, MT_MIB_SDR11(phy->band_idx));
+ cnt = mt76_rr(dev, MT_MIB_SDR11(band));
mib->rx_len_mismatch_cnt +=
FIELD_GET(MT_MIB_SDR11_RX_LEN_MISMATCH_CNT_MASK, cnt);
- cnt = mt76_rr(dev, MT_MIB_SDR12(phy->band_idx));
+ cnt = mt76_rr(dev, MT_MIB_SDR12(band));
mib->tx_ampdu_cnt += cnt;
- cnt = mt76_rr(dev, MT_MIB_SDR13(phy->band_idx));
+ cnt = mt76_rr(dev, MT_MIB_SDR13(band));
mib->tx_stop_q_empty_cnt +=
FIELD_GET(MT_MIB_SDR13_TX_STOP_Q_EMPTY_CNT_MASK, cnt);
- cnt = mt76_rr(dev, MT_MIB_SDR14(phy->band_idx));
+ cnt = mt76_rr(dev, MT_MIB_SDR14(band));
mib->tx_mpdu_attempts_cnt += is_mt7915(&dev->mt76) ?
FIELD_GET(MT_MIB_SDR14_TX_MPDU_ATTEMPTS_CNT_MASK, cnt) :
FIELD_GET(MT_MIB_SDR14_TX_MPDU_ATTEMPTS_CNT_MASK_MT7916, cnt);
- cnt = mt76_rr(dev, MT_MIB_SDR15(phy->band_idx));
+ cnt = mt76_rr(dev, MT_MIB_SDR15(band));
mib->tx_mpdu_success_cnt += is_mt7915(&dev->mt76) ?
FIELD_GET(MT_MIB_SDR15_TX_MPDU_SUCCESS_CNT_MASK, cnt) :
FIELD_GET(MT_MIB_SDR15_TX_MPDU_SUCCESS_CNT_MASK_MT7916, cnt);
- cnt = mt76_rr(dev, MT_MIB_SDR16(phy->band_idx));
+ cnt = mt76_rr(dev, MT_MIB_SDR16(band));
mib->primary_cca_busy_time +=
FIELD_GET(MT_MIB_SDR16_PRIMARY_CCA_BUSY_TIME_MASK, cnt);
- cnt = mt76_rr(dev, MT_MIB_SDR17(phy->band_idx));
+ cnt = mt76_rr(dev, MT_MIB_SDR17(band));
mib->secondary_cca_busy_time +=
FIELD_GET(MT_MIB_SDR17_SECONDARY_CCA_BUSY_TIME_MASK, cnt);
- cnt = mt76_rr(dev, MT_MIB_SDR18(phy->band_idx));
+ cnt = mt76_rr(dev, MT_MIB_SDR18(band));
mib->primary_energy_detect_time +=
FIELD_GET(MT_MIB_SDR18_PRIMARY_ENERGY_DETECT_TIME_MASK, cnt);
- cnt = mt76_rr(dev, MT_MIB_SDR19(phy->band_idx));
+ cnt = mt76_rr(dev, MT_MIB_SDR19(band));
mib->cck_mdrdy_time += FIELD_GET(MT_MIB_SDR19_CCK_MDRDY_TIME_MASK, cnt);
- cnt = mt76_rr(dev, MT_MIB_SDR20(phy->band_idx));
+ cnt = mt76_rr(dev, MT_MIB_SDR20(band));
mib->ofdm_mdrdy_time +=
FIELD_GET(MT_MIB_SDR20_OFDM_VHT_MDRDY_TIME_MASK, cnt);
- cnt = mt76_rr(dev, MT_MIB_SDR21(phy->band_idx));
+ cnt = mt76_rr(dev, MT_MIB_SDR21(band));
mib->green_mdrdy_time +=
FIELD_GET(MT_MIB_SDR21_GREEN_MDRDY_TIME_MASK, cnt);
- cnt = mt76_rr(dev, MT_MIB_SDR22(phy->band_idx));
+ cnt = mt76_rr(dev, MT_MIB_SDR22(band));
mib->rx_ampdu_cnt += cnt;
- cnt = mt76_rr(dev, MT_MIB_SDR23(phy->band_idx));
+ cnt = mt76_rr(dev, MT_MIB_SDR23(band));
mib->rx_ampdu_bytes_cnt += cnt;
- cnt = mt76_rr(dev, MT_MIB_SDR24(phy->band_idx));
+ cnt = mt76_rr(dev, MT_MIB_SDR24(band));
mib->rx_ampdu_valid_subframe_cnt += is_mt7915(&dev->mt76) ?
FIELD_GET(MT_MIB_SDR24_RX_AMPDU_SF_CNT_MASK, cnt) :
FIELD_GET(MT_MIB_SDR24_RX_AMPDU_SF_CNT_MASK_MT7916, cnt);
- cnt = mt76_rr(dev, MT_MIB_SDR25(phy->band_idx));
+ cnt = mt76_rr(dev, MT_MIB_SDR25(band));
mib->rx_ampdu_valid_subframe_bytes_cnt += cnt;
- cnt = mt76_rr(dev, MT_MIB_SDR27(phy->band_idx));
+ cnt = mt76_rr(dev, MT_MIB_SDR27(band));
mib->tx_rwp_fail_cnt +=
FIELD_GET(MT_MIB_SDR27_TX_RWP_FAIL_CNT_MASK, cnt);
- cnt = mt76_rr(dev, MT_MIB_SDR28(phy->band_idx));
+ cnt = mt76_rr(dev, MT_MIB_SDR28(band));
mib->tx_rwp_need_cnt +=
FIELD_GET(MT_MIB_SDR28_TX_RWP_NEED_CNT_MASK, cnt);
- cnt = mt76_rr(dev, MT_MIB_SDR29(phy->band_idx));
+ cnt = mt76_rr(dev, MT_MIB_SDR29(band));
mib->rx_pfdrop_cnt += is_mt7915(&dev->mt76) ?
FIELD_GET(MT_MIB_SDR29_RX_PFDROP_CNT_MASK, cnt) :
FIELD_GET(MT_MIB_SDR29_RX_PFDROP_CNT_MASK_MT7916, cnt);
- cnt = mt76_rr(dev, MT_MIB_SDRVEC(phy->band_idx));
+ cnt = mt76_rr(dev, MT_MIB_SDRVEC(band));
mib->rx_vec_queue_overflow_drop_cnt += is_mt7915(&dev->mt76) ?
FIELD_GET(MT_MIB_SDR30_RX_VEC_QUEUE_OVERFLOW_DROP_CNT_MASK, cnt) :
FIELD_GET(MT_MIB_SDR30_RX_VEC_QUEUE_OVERFLOW_DROP_CNT_MASK_MT7916, cnt);
- cnt = mt76_rr(dev, MT_MIB_SDR31(phy->band_idx));
+ cnt = mt76_rr(dev, MT_MIB_SDR31(band));
mib->rx_ba_cnt += cnt;
- cnt = mt76_rr(dev, MT_MIB_SDRMUBF(phy->band_idx));
+ cnt = mt76_rr(dev, MT_MIB_SDRMUBF(band));
mib->tx_bf_cnt += FIELD_GET(MT_MIB_MU_BF_TX_CNT, cnt);
- cnt = mt76_rr(dev, MT_MIB_DR8(phy->band_idx));
+ cnt = mt76_rr(dev, MT_MIB_DR8(band));
mib->tx_mu_mpdu_cnt += cnt;
- cnt = mt76_rr(dev, MT_MIB_DR9(phy->band_idx));
+ cnt = mt76_rr(dev, MT_MIB_DR9(band));
mib->tx_mu_acked_mpdu_cnt += cnt;
- cnt = mt76_rr(dev, MT_MIB_DR11(phy->band_idx));
+ cnt = mt76_rr(dev, MT_MIB_DR11(band));
mib->tx_su_acked_mpdu_cnt += cnt;
- cnt = mt76_rr(dev, MT_ETBF_PAR_RPT0(phy->band_idx));
+ cnt = mt76_rr(dev, MT_ETBF_PAR_RPT0(band));
mib->tx_bf_rx_fb_bw = FIELD_GET(MT_ETBF_PAR_RPT0_FB_BW, cnt);
mib->tx_bf_rx_fb_nc_cnt += FIELD_GET(MT_ETBF_PAR_RPT0_FB_NC, cnt);
mib->tx_bf_rx_fb_nr_cnt += FIELD_GET(MT_ETBF_PAR_RPT0_FB_NR, cnt);
@@ -1598,44 +1884,43 @@ void mt7915_mac_update_stats(struct mt7915_phy *phy)
mib->tx_amsdu_cnt += cnt;
}
- aggr0 = phy->band_idx ? ARRAY_SIZE(dev->mt76.aggr_stats) / 2 : 0;
if (is_mt7915(&dev->mt76)) {
- for (i = 0, aggr1 = aggr0 + 4; i < 4; i++) {
- val = mt76_rr(dev, MT_MIB_MB_SDR1(phy->band_idx, (i << 4)));
+ for (i = 0, aggr1 = aggr0 + 8; i < 4; i++) {
+ val = mt76_rr(dev, MT_MIB_MB_SDR1(band, (i << 4)));
mib->ba_miss_cnt +=
FIELD_GET(MT_MIB_BA_MISS_COUNT_MASK, val);
mib->ack_fail_cnt +=
FIELD_GET(MT_MIB_ACK_FAIL_COUNT_MASK, val);
- val = mt76_rr(dev, MT_MIB_MB_SDR0(phy->band_idx, (i << 4)));
+ val = mt76_rr(dev, MT_MIB_MB_SDR0(band, (i << 4)));
mib->rts_cnt += FIELD_GET(MT_MIB_RTS_COUNT_MASK, val);
mib->rts_retries_cnt +=
FIELD_GET(MT_MIB_RTS_RETRIES_COUNT_MASK, val);
- val = mt76_rr(dev, MT_TX_AGG_CNT(phy->band_idx, i));
- dev->mt76.aggr_stats[aggr0++] += val & 0xffff;
- dev->mt76.aggr_stats[aggr0++] += val >> 16;
+ val = mt76_rr(dev, MT_TX_AGG_CNT(band, i));
+ phy->mt76->aggr_stats[aggr0++] += val & 0xffff;
+ phy->mt76->aggr_stats[aggr0++] += val >> 16;
- val = mt76_rr(dev, MT_TX_AGG_CNT2(phy->band_idx, i));
- dev->mt76.aggr_stats[aggr1++] += val & 0xffff;
- dev->mt76.aggr_stats[aggr1++] += val >> 16;
+ val = mt76_rr(dev, MT_TX_AGG_CNT2(band, i));
+ phy->mt76->aggr_stats[aggr1++] += val & 0xffff;
+ phy->mt76->aggr_stats[aggr1++] += val >> 16;
}
- cnt = mt76_rr(dev, MT_MIB_SDR32(phy->band_idx));
+ cnt = mt76_rr(dev, MT_MIB_SDR32(band));
mib->tx_pkt_ebf_cnt += FIELD_GET(MT_MIB_SDR32_TX_PKT_EBF_CNT, cnt);
- cnt = mt76_rr(dev, MT_MIB_SDR33(phy->band_idx));
+ cnt = mt76_rr(dev, MT_MIB_SDR33(band));
mib->tx_pkt_ibf_cnt += FIELD_GET(MT_MIB_SDR33_TX_PKT_IBF_CNT, cnt);
- cnt = mt76_rr(dev, MT_ETBF_TX_APP_CNT(phy->band_idx));
+ cnt = mt76_rr(dev, MT_ETBF_TX_APP_CNT(band));
mib->tx_bf_ibf_ppdu_cnt += FIELD_GET(MT_ETBF_TX_IBF_CNT, cnt);
mib->tx_bf_ebf_ppdu_cnt += FIELD_GET(MT_ETBF_TX_EBF_CNT, cnt);
- cnt = mt76_rr(dev, MT_ETBF_TX_NDP_BFRP(phy->band_idx));
+ cnt = mt76_rr(dev, MT_ETBF_TX_NDP_BFRP(band));
mib->tx_bf_fb_cpl_cnt += FIELD_GET(MT_ETBF_TX_FB_CPL, cnt);
mib->tx_bf_fb_trig_cnt += FIELD_GET(MT_ETBF_TX_FB_TRI, cnt);
- cnt = mt76_rr(dev, MT_ETBF_RX_FB_CNT(phy->band_idx));
+ cnt = mt76_rr(dev, MT_ETBF_RX_FB_CNT(band));
mib->tx_bf_rx_fb_all_cnt += FIELD_GET(MT_ETBF_RX_FB_ALL, cnt);
mib->tx_bf_rx_fb_he_cnt += FIELD_GET(MT_ETBF_RX_FB_HE, cnt);
mib->tx_bf_rx_fb_vht_cnt += FIELD_GET(MT_ETBF_RX_FB_VHT, cnt);
@@ -1643,51 +1928,51 @@ void mt7915_mac_update_stats(struct mt7915_phy *phy)
} else {
for (i = 0; i < 2; i++) {
/* rts count */
- val = mt76_rr(dev, MT_MIB_MB_SDR0(phy->band_idx, (i << 2)));
+ val = mt76_rr(dev, MT_MIB_MB_SDR0(band, (i << 2)));
mib->rts_cnt += FIELD_GET(GENMASK(15, 0), val);
mib->rts_cnt += FIELD_GET(GENMASK(31, 16), val);
/* rts retry count */
- val = mt76_rr(dev, MT_MIB_MB_SDR1(phy->band_idx, (i << 2)));
+ val = mt76_rr(dev, MT_MIB_MB_SDR1(band, (i << 2)));
mib->rts_retries_cnt += FIELD_GET(GENMASK(15, 0), val);
mib->rts_retries_cnt += FIELD_GET(GENMASK(31, 16), val);
/* ba miss count */
- val = mt76_rr(dev, MT_MIB_MB_SDR2(phy->band_idx, (i << 2)));
+ val = mt76_rr(dev, MT_MIB_MB_SDR2(band, (i << 2)));
mib->ba_miss_cnt += FIELD_GET(GENMASK(15, 0), val);
mib->ba_miss_cnt += FIELD_GET(GENMASK(31, 16), val);
/* ack fail count */
- val = mt76_rr(dev, MT_MIB_MB_BFTF(phy->band_idx, (i << 2)));
+ val = mt76_rr(dev, MT_MIB_MB_BFTF(band, (i << 2)));
mib->ack_fail_cnt += FIELD_GET(GENMASK(15, 0), val);
mib->ack_fail_cnt += FIELD_GET(GENMASK(31, 16), val);
}
for (i = 0; i < 8; i++) {
- val = mt76_rr(dev, MT_TX_AGG_CNT(phy->band_idx, i));
- dev->mt76.aggr_stats[aggr0++] += FIELD_GET(GENMASK(15, 0), val);
- dev->mt76.aggr_stats[aggr0++] += FIELD_GET(GENMASK(31, 16), val);
+ val = mt76_rr(dev, MT_TX_AGG_CNT(band, i));
+ phy->mt76->aggr_stats[aggr0++] += FIELD_GET(GENMASK(15, 0), val);
+ phy->mt76->aggr_stats[aggr0++] += FIELD_GET(GENMASK(31, 16), val);
}
- cnt = mt76_rr(dev, MT_MIB_SDR32(phy->band_idx));
+ cnt = mt76_rr(dev, MT_MIB_SDR32(band));
mib->tx_pkt_ibf_cnt += FIELD_GET(MT_MIB_SDR32_TX_PKT_IBF_CNT, cnt);
mib->tx_bf_ibf_ppdu_cnt += FIELD_GET(MT_MIB_SDR32_TX_PKT_IBF_CNT, cnt);
mib->tx_pkt_ebf_cnt += FIELD_GET(MT_MIB_SDR32_TX_PKT_EBF_CNT, cnt);
mib->tx_bf_ebf_ppdu_cnt += FIELD_GET(MT_MIB_SDR32_TX_PKT_EBF_CNT, cnt);
- cnt = mt76_rr(dev, MT_MIB_BFCR7(phy->band_idx));
+ cnt = mt76_rr(dev, MT_MIB_BFCR7(band));
mib->tx_bf_fb_cpl_cnt += FIELD_GET(MT_MIB_BFCR7_BFEE_TX_FB_CPL, cnt);
- cnt = mt76_rr(dev, MT_MIB_BFCR2(phy->band_idx));
+ cnt = mt76_rr(dev, MT_MIB_BFCR2(band));
mib->tx_bf_fb_trig_cnt += FIELD_GET(MT_MIB_BFCR2_BFEE_TX_FB_TRIG, cnt);
- cnt = mt76_rr(dev, MT_MIB_BFCR0(phy->band_idx));
+ cnt = mt76_rr(dev, MT_MIB_BFCR0(band));
mib->tx_bf_rx_fb_vht_cnt += FIELD_GET(MT_MIB_BFCR0_RX_FB_VHT, cnt);
mib->tx_bf_rx_fb_all_cnt += FIELD_GET(MT_MIB_BFCR0_RX_FB_VHT, cnt);
mib->tx_bf_rx_fb_ht_cnt += FIELD_GET(MT_MIB_BFCR0_RX_FB_HT, cnt);
mib->tx_bf_rx_fb_all_cnt += FIELD_GET(MT_MIB_BFCR0_RX_FB_HT, cnt);
- cnt = mt76_rr(dev, MT_MIB_BFCR1(phy->band_idx));
+ cnt = mt76_rr(dev, MT_MIB_BFCR1(band));
mib->tx_bf_rx_fb_he_cnt += FIELD_GET(MT_MIB_BFCR1_RX_FB_HE, cnt);
mib->tx_bf_rx_fb_all_cnt += FIELD_GET(MT_MIB_BFCR1_RX_FB_HE, cnt);
}
@@ -1696,7 +1981,6 @@ void mt7915_mac_update_stats(struct mt7915_phy *phy)
static void mt7915_mac_severe_check(struct mt7915_phy *phy)
{
struct mt7915_dev *dev = phy->dev;
- bool ext_phy = phy != &dev->phy;
u32 trb;
if (!phy->omac_mask)
@@ -1706,7 +1990,7 @@ static void mt7915_mac_severe_check(struct mt7915_phy *phy)
* stopping Rx, so check status periodically to see if TRB hardware
* requires minimal recovery.
*/
- trb = mt76_rr(dev, MT_TRB_RXPSR0(phy->band_idx));
+ trb = mt76_rr(dev, MT_TRB_RXPSR0(phy->mt76->band_idx));
if ((FIELD_GET(MT_TRB_RXPSR0_RX_RMAC_PTR, trb) !=
FIELD_GET(MT_TRB_RXPSR0_RX_WTBL_PTR, trb)) &&
@@ -1714,7 +1998,7 @@ static void mt7915_mac_severe_check(struct mt7915_phy *phy)
FIELD_GET(MT_TRB_RXPSR0_RX_WTBL_PTR, phy->trb_ts)) &&
trb == phy->trb_ts)
mt7915_mcu_set_ser(dev, SER_RECOVER, SER_SET_RECOVER_L3_RX_ABORT,
- ext_phy);
+ phy->mt76->band_idx);
phy->trb_ts = trb;
}
@@ -1816,6 +2100,13 @@ static int mt7915_dfs_start_rdd(struct mt7915_dev *dev, int chain)
if (err < 0)
return err;
+ if (is_mt7915(&dev->mt76)) {
+ err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_SET_WF_ANT, chain,
+ 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);
}
@@ -1827,16 +2118,16 @@ static int mt7915_dfs_start_radar_detector(struct mt7915_phy *phy)
int err;
/* start CAC */
- err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_CAC_START, phy->band_idx,
- MT_RX_SEL0, 0);
+ err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_CAC_START,
+ phy->mt76->band_idx, MT_RX_SEL0, 0);
if (err < 0)
return err;
- err = mt7915_dfs_start_rdd(dev, phy->band_idx);
+ err = mt7915_dfs_start_rdd(dev, phy->mt76->band_idx);
if (err < 0)
return err;
- phy->rdd_state |= BIT(phy->band_idx);
+ phy->rdd_state |= BIT(phy->mt76->band_idx);
if (!is_mt7915(&dev->mt76))
return 0;
@@ -1921,7 +2212,7 @@ int mt7915_dfs_init_radar_detector(struct mt7915_phy *phy)
return 0;
err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_CAC_END,
- phy->band_idx, MT_RX_SEL0, 0);
+ phy->mt76->band_idx, MT_RX_SEL0, 0);
if (err < 0) {
phy->mt76->dfs_state = MT_DFS_STATE_UNKNOWN;
return err;
@@ -1932,10 +2223,18 @@ int mt7915_dfs_init_radar_detector(struct mt7915_phy *phy)
stop:
err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_NORMAL_START,
- phy->band_idx, MT_RX_SEL0, 0);
+ phy->mt76->band_idx, MT_RX_SEL0, 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);
+ if (err < 0)
+ return err;
+ }
+
mt7915_dfs_stop_radar_detector(phy);
phy->mt76->dfs_state = MT_DFS_STATE_DISABLED;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/main.c b/drivers/net/wireless/mediatek/mt76/mt7915/main.c
index 89b519cfd14c..0511d6a505b0 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/main.c
@@ -20,45 +20,45 @@ static bool mt7915_dev_running(struct mt7915_dev *dev)
return phy && test_bit(MT76_STATE_RUNNING, &phy->mt76->state);
}
-static int mt7915_start(struct ieee80211_hw *hw)
+int mt7915_run(struct ieee80211_hw *hw)
{
struct mt7915_dev *dev = mt7915_hw_dev(hw);
struct mt7915_phy *phy = mt7915_hw_phy(hw);
bool running;
int ret;
- flush_work(&dev->init_work);
-
- mutex_lock(&dev->mt76.mutex);
-
running = mt7915_dev_running(dev);
if (!running) {
- ret = mt76_connac_mcu_set_pm(&dev->mt76, 0, 0);
+ ret = mt76_connac_mcu_set_pm(&dev->mt76,
+ dev->phy.mt76->band_idx, 0);
if (ret)
goto out;
- ret = mt7915_mcu_set_mac(dev, 0, true, true);
+ ret = mt7915_mcu_set_mac(dev, dev->phy.mt76->band_idx,
+ true, true);
if (ret)
goto out;
- mt7915_mac_enable_nf(dev, 0);
+ mt7915_mac_enable_nf(dev, dev->phy.mt76->band_idx);
}
- if (phy != &dev->phy || phy->band_idx) {
- ret = mt76_connac_mcu_set_pm(&dev->mt76, 1, 0);
+ if (phy != &dev->phy) {
+ ret = mt76_connac_mcu_set_pm(&dev->mt76,
+ phy->mt76->band_idx, 0);
if (ret)
goto out;
- ret = mt7915_mcu_set_mac(dev, 1, true, true);
+ ret = mt7915_mcu_set_mac(dev, phy->mt76->band_idx,
+ true, true);
if (ret)
goto out;
- mt7915_mac_enable_nf(dev, 1);
+ mt7915_mac_enable_nf(dev, phy->mt76->band_idx);
}
ret = mt76_connac_mcu_set_rts_thresh(&dev->mt76, 0x92b,
- phy != &dev->phy);
+ phy->mt76->band_idx);
if (ret)
goto out;
@@ -80,6 +80,18 @@ static int mt7915_start(struct ieee80211_hw *hw)
mt7915_mac_reset_counters(phy);
out:
+ return ret;
+}
+
+static int mt7915_start(struct ieee80211_hw *hw)
+{
+ struct mt7915_dev *dev = mt7915_hw_dev(hw);
+ int ret;
+
+ flush_work(&dev->init_work);
+
+ mutex_lock(&dev->mt76.mutex);
+ ret = mt7915_run(hw);
mutex_unlock(&dev->mt76.mutex);
return ret;
@@ -99,13 +111,13 @@ static void mt7915_stop(struct ieee80211_hw *hw)
clear_bit(MT76_STATE_RUNNING, &phy->mt76->state);
if (phy != &dev->phy) {
- mt76_connac_mcu_set_pm(&dev->mt76, 1, 1);
- mt7915_mcu_set_mac(dev, 1, false, false);
+ mt76_connac_mcu_set_pm(&dev->mt76, phy->mt76->band_idx, 1);
+ mt7915_mcu_set_mac(dev, phy->mt76->band_idx, false, false);
}
if (!mt7915_dev_running(dev)) {
- mt76_connac_mcu_set_pm(&dev->mt76, 0, 1);
- mt7915_mcu_set_mac(dev, 0, false, false);
+ mt76_connac_mcu_set_pm(&dev->mt76, dev->phy.mt76->band_idx, 1);
+ mt7915_mcu_set_mac(dev, dev->phy.mt76->band_idx, false, false);
}
mutex_unlock(&dev->mt76.mutex);
@@ -209,7 +221,7 @@ static int mt7915_add_interface(struct ieee80211_hw *hw,
}
mvif->mt76.omac_idx = idx;
mvif->phy = phy;
- mvif->mt76.band_idx = phy->band_idx;
+ mvif->mt76.band_idx = phy->mt76->band_idx;
mvif->mt76.wmm_idx = vif->type != NL80211_IFTYPE_AP;
if (ext_phy)
@@ -432,7 +444,6 @@ static int mt7915_config(struct ieee80211_hw *hw, u32 changed)
{
struct mt7915_dev *dev = mt7915_hw_dev(hw);
struct mt7915_phy *phy = mt7915_hw_phy(hw);
- bool band = phy != &dev->phy;
int ret;
if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
@@ -460,6 +471,7 @@ static int mt7915_config(struct ieee80211_hw *hw, u32 changed)
if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
bool enabled = !!(hw->conf.flags & IEEE80211_CONF_MONITOR);
+ bool band = phy->mt76->band_idx;
if (!enabled)
phy->rxfilter |= MT_WF_RFCR_DROP_OTHER_UC;
@@ -498,7 +510,7 @@ static void mt7915_configure_filter(struct ieee80211_hw *hw,
{
struct mt7915_dev *dev = mt7915_hw_dev(hw);
struct mt7915_phy *phy = mt7915_hw_phy(hw);
- bool band = phy != &dev->phy;
+ bool band = phy->mt76->band_idx;
u32 ctl_flags = MT_WF_RFCR1_DROP_ACK |
MT_WF_RFCR1_DROP_BF_POLL |
MT_WF_RFCR1_DROP_BA |
@@ -593,10 +605,11 @@ static void mt7915_bss_info_changed(struct ieee80211_hw *hw,
mt7915_mcu_add_sta(dev, vif, NULL, join);
}
- if (changed & BSS_CHANGED_ASSOC) {
+ if (changed & BSS_CHANGED_ASSOC)
mt7915_mcu_add_bss_info(phy, vif, vif->cfg.assoc);
- mt7915_mcu_add_obss_spr(dev, vif, info->he_obss_pd.enable);
- }
+
+ if (changed & BSS_CHANGED_ERP_CTS_PROT)
+ mt7915_mac_enable_rtscts(dev, vif, info->use_cts_prot);
if (changed & BSS_CHANGED_ERP_SLOT) {
int slottime = info->use_short_slot ? 9 : 20;
@@ -617,7 +630,7 @@ static void mt7915_bss_info_changed(struct ieee80211_hw *hw,
mt7915_mcu_set_tx(dev, vif);
if (changed & BSS_CHANGED_HE_OBSS_PD)
- mt7915_mcu_add_obss_spr(dev, vif, info->he_obss_pd.enable);
+ mt7915_mcu_add_obss_spr(phy, vif, &info->he_obss_pd);
if (changed & BSS_CHANGED_HE_BSS_COLOR)
mt7915_update_bss_color(hw, vif, &info->he_bss_color);
@@ -665,6 +678,8 @@ int mt7915_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
msta->wcid.tx_info |= MT_WCID_TX_INFO_SET;
msta->jiffies = jiffies;
+ ewma_avg_signal_init(&msta->avg_ack_signal);
+
mt7915_mac_wtbl_update(dev, idx,
MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
@@ -732,7 +747,8 @@ static int mt7915_set_rts_threshold(struct ieee80211_hw *hw, u32 val)
int ret;
mutex_lock(&dev->mt76.mutex);
- ret = mt76_connac_mcu_set_rts_thresh(&dev->mt76, val, phy != &dev->phy);
+ ret = mt76_connac_mcu_set_rts_thresh(&dev->mt76, val,
+ phy->mt76->band_idx);
mutex_unlock(&dev->mt76.mutex);
return ret;
@@ -835,7 +851,7 @@ u64 __mt7915_get_tsf(struct ieee80211_hw *hw, struct mt7915_vif *mvif)
{
struct mt7915_dev *dev = mt7915_hw_dev(hw);
struct mt7915_phy *phy = mt7915_hw_phy(hw);
- bool band = phy != &dev->phy;
+ bool band = phy->mt76->band_idx;
union {
u64 t64;
u32 t32[2];
@@ -880,7 +896,7 @@ mt7915_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv;
struct mt7915_dev *dev = mt7915_hw_dev(hw);
struct mt7915_phy *phy = mt7915_hw_phy(hw);
- bool band = phy != &dev->phy;
+ bool band = phy->mt76->band_idx;
union {
u64 t64;
u32 t32[2];
@@ -911,7 +927,7 @@ mt7915_offset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv;
struct mt7915_dev *dev = mt7915_hw_dev(hw);
struct mt7915_phy *phy = mt7915_hw_phy(hw);
- bool band = phy != &dev->phy;
+ bool band = phy->mt76->band_idx;
union {
u64 t64;
u32 t32[2];
@@ -953,22 +969,21 @@ mt7915_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant)
struct mt7915_dev *dev = mt7915_hw_dev(hw);
struct mt7915_phy *phy = mt7915_hw_phy(hw);
int max_nss = hweight8(hw->wiphy->available_antennas_tx);
- bool ext_phy = phy != &dev->phy;
+ u8 chainshift = dev->chainshift;
+ u8 band = phy->mt76->band_idx;
if (!tx_ant || tx_ant != rx_ant || ffs(tx_ant) > max_nss)
return -EINVAL;
- if ((BIT(hweight8(tx_ant)) - 1) != tx_ant)
- tx_ant = BIT(ffs(tx_ant) - 1) - 1;
-
mutex_lock(&dev->mt76.mutex);
phy->mt76->antenna_mask = tx_ant;
- if (ext_phy)
- tx_ant <<= dev->chainshift;
-
- phy->mt76->chainmask = tx_ant;
+ /* handle a variant of mt7916 which has 3T3R but nss2 on 5 GHz band */
+ if (is_mt7916(&dev->mt76) && band && hweight8(tx_ant) == max_nss)
+ phy->mt76->chainmask = (dev->chainmask >> chainshift) << chainshift;
+ else
+ phy->mt76->chainmask = tx_ant << (chainshift * band);
mt76_set_stream_caps(phy->mt76, true);
mt7915_set_stream_vht_txbf_caps(phy);
@@ -1026,7 +1041,21 @@ static void mt7915_sta_statistics(struct ieee80211_hw *hw,
sinfo->tx_retries = msta->wcid.stats.tx_retries;
sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_RETRIES);
+
+ if (mtk_wed_get_rx_capa(&phy->dev->mt76.mmio.wed)) {
+ sinfo->rx_bytes = msta->wcid.stats.rx_bytes;
+ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BYTES64);
+
+ sinfo->rx_packets = msta->wcid.stats.rx_packets;
+ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_PACKETS);
+ }
}
+
+ sinfo->ack_signal = (s8)msta->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->filled |= BIT_ULL(NL80211_STA_INFO_ACK_SIGNAL_AVG);
}
static void mt7915_sta_rc_work(void *data, struct ieee80211_sta *sta)
@@ -1111,6 +1140,39 @@ static void mt7915_sta_set_decap_offload(struct ieee80211_hw *hw,
mt76_connac_mcu_wtbl_update_hdr_trans(&dev->mt76, vif, sta);
}
+static int mt7915_sta_set_txpwr(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct mt7915_phy *phy = mt7915_hw_phy(hw);
+ struct mt7915_dev *dev = mt7915_hw_dev(hw);
+ s16 txpower = sta->deflink.txpwr.power;
+ int ret;
+
+ if (sta->deflink.txpwr.type == NL80211_TX_POWER_AUTOMATIC)
+ txpower = 0;
+
+ mutex_lock(&dev->mt76.mutex);
+
+ /* NOTE: temporarily use 0 as minimum limit, which is a
+ * global setting and will be applied to all stations.
+ */
+ ret = mt7915_mcu_set_txpower_frame_min(phy, 0);
+ if (ret)
+ goto out;
+
+ /* This only applies to data frames while pushing traffic,
+ * whereas the management frames or other packets that are
+ * using fixed rate can be configured via TxD.
+ */
+ ret = mt7915_mcu_set_txpower_frame(phy, vif, sta, txpower);
+
+out:
+ mutex_unlock(&dev->mt76.mutex);
+
+ return ret;
+}
+
static const char mt7915_gstrings_stats[][ETH_GSTRING_LEN] = {
"tx_ampdu_cnt",
"tx_stop_q_empty_cnt",
@@ -1258,7 +1320,7 @@ void mt7915_get_et_stats(struct ieee80211_hw *hw,
};
struct mib_stats *mib = &phy->mib;
/* See mt7915_ampdu_stat_read_phy, etc */
- int i, n, ei = 0;
+ int i, ei = 0;
mutex_lock(&dev->mt76.mutex);
@@ -1274,9 +1336,8 @@ void mt7915_get_et_stats(struct ieee80211_hw *hw,
data[ei++] = mib->tx_pkt_ibf_cnt;
/* Tx ampdu stat */
- n = phy->band_idx ? ARRAY_SIZE(dev->mt76.aggr_stats) / 2 : 0;
for (i = 0; i < 15 /*ARRAY_SIZE(bound)*/; i++)
- data[ei++] = dev->mt76.aggr_stats[i + n];
+ data[ei++] = phy->mt76->aggr_stats[i];
data[ei++] = phy->mib.ba_miss_cnt;
@@ -1431,7 +1492,7 @@ mt7915_net_fill_forward_path(struct ieee80211_hw *hw,
path->dev = ctx->dev;
path->mtk_wdma.wdma_idx = wed->wdma_idx;
path->mtk_wdma.bss = mvif->mt76.idx;
- path->mtk_wdma.wcid = msta->wcid.idx;
+ path->mtk_wdma.wcid = is_mt7915(&dev->mt76) ? msta->wcid.idx : 0x3ff;
path->mtk_wdma.queue = phy != &dev->phy;
ctx->dev = NULL;
@@ -1477,6 +1538,7 @@ const struct ieee80211_ops mt7915_ops = {
.set_bitrate_mask = mt7915_set_bitrate_mask,
.set_coverage_class = mt7915_set_coverage_class,
.sta_statistics = mt7915_sta_statistics,
+ .sta_set_txpwr = mt7915_sta_set_txpwr,
.sta_set_4addr = mt7915_sta_set_4addr,
.sta_set_decap_offload = mt7915_sta_set_decap_offload,
.add_twt_setup = mt7915_mac_add_twt_setup,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
index 8d297e4aa7d4..b2652de082ba 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
@@ -32,6 +32,10 @@
#define HE_PHY(p, c) u8_get_bits(c, IEEE80211_HE_PHY_##p)
#define HE_MAC(m, c) u8_get_bits(c, IEEE80211_HE_MAC_##m)
+static bool sr_scene_detect = true;
+module_param(sr_scene_detect, bool, 0644);
+MODULE_PARM_DESC(sr_scene_detect, "Enable firmware scene detection algorithm");
+
static u8
mt7915_mcu_get_sta_nss(u16 mcs_map)
{
@@ -228,7 +232,8 @@ mt7915_mcu_rx_csa_notify(struct mt7915_dev *dev, struct sk_buff *skb)
c = (struct mt7915_mcu_csa_notify *)skb->data;
- if ((c->band_idx && !dev->phy.band_idx) && dev->mt76.phys[MT_BAND1])
+ if ((c->band_idx && !dev->phy.mt76->band_idx) &&
+ dev->mt76.phys[MT_BAND1])
mphy = dev->mt76.phys[MT_BAND1];
ieee80211_iterate_active_interfaces_atomic(mphy->hw,
@@ -247,7 +252,8 @@ mt7915_mcu_rx_thermal_notify(struct mt7915_dev *dev, struct sk_buff *skb)
if (t->ctrl.ctrl_id != THERMAL_PROTECT_ENABLE)
return;
- if ((t->ctrl.band_idx && !dev->phy.band_idx) && dev->mt76.phys[MT_BAND1])
+ if ((t->ctrl.band_idx && !dev->phy.mt76->band_idx) &&
+ dev->mt76.phys[MT_BAND1])
mphy = dev->mt76.phys[MT_BAND1];
phy = (struct mt7915_phy *)mphy->priv;
@@ -262,7 +268,8 @@ mt7915_mcu_rx_radar_detected(struct mt7915_dev *dev, struct sk_buff *skb)
r = (struct mt7915_mcu_rdd_report *)skb->data;
- if ((r->band_idx && !dev->phy.band_idx) && dev->mt76.phys[MT_BAND1])
+ if ((r->band_idx && !dev->phy.mt76->band_idx) &&
+ dev->mt76.phys[MT_BAND1])
mphy = dev->mt76.phys[MT_BAND1];
if (r->band_idx == MT_RX_SEL2)
@@ -319,7 +326,7 @@ mt7915_mcu_rx_bcc_notify(struct mt7915_dev *dev, struct sk_buff *skb)
b = (struct mt7915_mcu_bcc_notify *)skb->data;
- if ((b->band_idx && !dev->phy.band_idx) && dev->mt76.phys[MT_BAND1])
+ if ((b->band_idx && !dev->phy.mt76->band_idx) && dev->mt76.phys[MT_BAND1])
mphy = dev->mt76.phys[MT_BAND1];
ieee80211_iterate_active_interfaces_atomic(mphy->hw,
@@ -485,7 +492,7 @@ static void
mt7915_mcu_bss_ra_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
struct mt7915_phy *phy)
{
- int max_nss = hweight8(phy->mt76->chainmask);
+ int max_nss = hweight8(phy->mt76->antenna_mask);
struct bss_info_ra *ra;
struct tlv *tlv;
@@ -595,7 +602,7 @@ mt7915_mcu_muar_config(struct mt7915_phy *phy, struct ieee80211_vif *vif,
.mode = !!mask || enable,
.entry_count = 1,
.write = 1,
- .band = phy != &dev->phy,
+ .band = phy->mt76->band_idx,
.index = idx * 2 + bssid,
};
@@ -1131,7 +1138,7 @@ mt7915_mcu_sta_bfer_he(struct ieee80211_sta *sta, struct ieee80211_vif *vif,
mcs_map = le16_to_cpu(pc->he_mcs_nss_supp.rx_mcs_160);
nss_mcs = mt7915_mcu_get_sta_nss(mcs_map);
- bf->ncol_bw160 = nss_mcs;
+ bf->ncol_gt_bw80 = nss_mcs;
}
if (pe->phy_cap_info[0] &
@@ -1139,10 +1146,10 @@ mt7915_mcu_sta_bfer_he(struct ieee80211_sta *sta, struct ieee80211_vif *vif,
mcs_map = le16_to_cpu(pc->he_mcs_nss_supp.rx_mcs_80p80);
nss_mcs = mt7915_mcu_get_sta_nss(mcs_map);
- if (bf->ncol_bw160)
- bf->ncol_bw160 = min_t(u8, bf->ncol_bw160, nss_mcs);
+ if (bf->ncol_gt_bw80)
+ bf->ncol_gt_bw80 = min_t(u8, bf->ncol_gt_bw80, nss_mcs);
else
- bf->ncol_bw160 = nss_mcs;
+ bf->ncol_gt_bw80 = nss_mcs;
}
snd_dim = HE_PHY(CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_MASK,
@@ -1150,7 +1157,7 @@ mt7915_mcu_sta_bfer_he(struct ieee80211_sta *sta, struct ieee80211_vif *vif,
sts = HE_PHY(CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_MASK,
pe->phy_cap_info[4]);
- bf->nrow_bw160 = min_t(int, snd_dim, sts);
+ bf->nrow_gt_bw80 = min_t(int, snd_dim, sts);
}
static void
@@ -1306,6 +1313,9 @@ int mt7915_mcu_set_fixed_rate_ctrl(struct mt7915_dev *dev,
case RATE_PARAM_MMPS_UPDATE:
ra->mmps_mode = mt7915_mcu_get_mmps_mode(sta->deflink.smps_mode);
break;
+ case RATE_PARAM_SPE_UPDATE:
+ ra->spe_idx = *(u8 *)data;
+ break;
default:
break;
}
@@ -1349,6 +1359,18 @@ int mt7915_mcu_add_smps(struct mt7915_dev *dev, struct ieee80211_vif *vif,
}
static int
+mt7915_mcu_set_spe_idx(struct mt7915_dev *dev, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv;
+ struct mt76_phy *mphy = mvif->phy->mt76;
+ u8 spe_idx = mt76_connac_spe_idx(mphy->antenna_mask);
+
+ return mt7915_mcu_set_fixed_rate_ctrl(dev, vif, sta, &spe_idx,
+ RATE_PARAM_SPE_UPDATE);
+}
+
+static int
mt7915_mcu_add_rate_ctrl_fixed(struct mt7915_dev *dev,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta)
@@ -1435,7 +1457,7 @@ mt7915_mcu_add_rate_ctrl_fixed(struct mt7915_dev *dev,
return ret;
}
- return 0;
+ return mt7915_mcu_set_spe_idx(dev, vif, sta);
}
static void
@@ -1662,10 +1684,32 @@ int mt7915_mcu_add_sta(struct mt7915_dev *dev, struct ieee80211_vif *vif,
return ret;
}
out:
+ ret = mt76_connac_mcu_sta_wed_update(&dev->mt76, skb);
+ if (ret)
+ return ret;
+
return mt76_mcu_skb_send_msg(&dev->mt76, skb,
MCU_EXT_CMD(STA_REC_UPDATE), true);
}
+int mt7915_mcu_wed_enable_rx_stats(struct mt7915_dev *dev)
+{
+#ifdef CONFIG_NET_MEDIATEK_SOC_WED
+ struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
+ struct {
+ __le32 args[2];
+ } req = {
+ .args[0] = cpu_to_le32(1),
+ .args[1] = cpu_to_le32(6),
+ };
+
+ return mtk_wed_device_update_msg(wed, MTK_WED_WO_CMD_RXCNT_CTRL,
+ &req, sizeof(req));
+#else
+ return 0;
+#endif
+}
+
int mt7915_mcu_add_dev_info(struct mt7915_phy *phy,
struct ieee80211_vif *vif, bool enable)
{
@@ -1674,7 +1718,7 @@ int mt7915_mcu_add_dev_info(struct mt7915_phy *phy,
struct {
struct req_hdr {
u8 omac_idx;
- u8 dbdc_idx;
+ u8 band_idx;
__le16 tlv_num;
u8 is_tlv_append;
u8 rsv[3];
@@ -1683,13 +1727,13 @@ int mt7915_mcu_add_dev_info(struct mt7915_phy *phy,
__le16 tag;
__le16 len;
u8 active;
- u8 dbdc_idx;
+ u8 band_idx;
u8 omac_addr[ETH_ALEN];
} __packed tlv;
} data = {
.hdr = {
.omac_idx = mvif->mt76.omac_idx,
- .dbdc_idx = mvif->mt76.band_idx,
+ .band_idx = mvif->mt76.band_idx,
.tlv_num = cpu_to_le16(1),
.is_tlv_append = 1,
},
@@ -1697,7 +1741,7 @@ int mt7915_mcu_add_dev_info(struct mt7915_phy *phy,
.tag = cpu_to_le16(DEV_INFO_ACTIVE),
.len = cpu_to_le16(sizeof(struct req_tlv)),
.active = enable,
- .dbdc_idx = mvif->mt76.band_idx,
+ .band_idx = mvif->mt76.band_idx,
},
};
@@ -2151,7 +2195,7 @@ int mt7915_mcu_muru_debug_get(struct mt7915_phy *phy, void *ms)
u8 band_idx;
} req = {
.cmd = cpu_to_le32(MURU_GET_TXC_TX_STATS),
- .band_idx = phy->band_idx,
+ .band_idx = phy->mt76->band_idx,
};
ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_EXT_CMD(MURU_CTRL),
@@ -2234,18 +2278,10 @@ mt7915_mcu_init_rx_airtime(struct mt7915_dev *dev)
sizeof(req), true);
}
-int mt7915_mcu_init(struct mt7915_dev *dev)
+int mt7915_mcu_init_firmware(struct mt7915_dev *dev)
{
- static const struct mt76_mcu_ops mt7915_mcu_ops = {
- .headroom = sizeof(struct mt76_connac2_mcu_txd),
- .mcu_skb_send_msg = mt7915_mcu_send_message,
- .mcu_parse_response = mt7915_mcu_parse_response,
- .mcu_restart = mt76_connac_mcu_restart,
- };
int ret;
- dev->mt76.mcu_ops = &mt7915_mcu_ops;
-
/* force firmware operation mode into normal state,
* which should be set before firmware download stage.
*/
@@ -2274,7 +2310,7 @@ int mt7915_mcu_init(struct mt7915_dev *dev)
if (ret)
return ret;
- if (mtk_wed_device_active(&dev->mt76.mmio.wed))
+ if (mtk_wed_device_active(&dev->mt76.mmio.wed) && is_mt7915(&dev->mt76))
mt7915_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(CAPABILITY), 0, 0, 0);
ret = mt7915_mcu_set_mwds(dev, 1);
@@ -2294,6 +2330,20 @@ int mt7915_mcu_init(struct mt7915_dev *dev)
MCU_WA_PARAM_RED, 0, 0);
}
+int mt7915_mcu_init(struct mt7915_dev *dev)
+{
+ static const struct mt76_mcu_ops mt7915_mcu_ops = {
+ .headroom = sizeof(struct mt76_connac2_mcu_txd),
+ .mcu_skb_send_msg = mt7915_mcu_send_message,
+ .mcu_parse_response = mt7915_mcu_parse_response,
+ .mcu_restart = mt76_connac_mcu_restart,
+ };
+
+ dev->mt76.mcu_ops = &mt7915_mcu_ops;
+
+ return mt7915_mcu_init_firmware(dev);
+}
+
void mt7915_mcu_exit(struct mt7915_dev *dev)
{
__mt76_mcu_restart(&dev->mt76);
@@ -2538,7 +2588,7 @@ mt7915_mcu_background_chain_ctrl(struct mt7915_phy *phy,
req.monitor_central_chan =
ieee80211_frequency_to_channel(chandef->center_freq1);
req.monitor_bw = mt76_connac_chan_bw(chandef);
- req.band_idx = phy != &dev->phy;
+ req.band_idx = phy->mt76->band_idx;
req.scan_mode = 1;
break;
}
@@ -2546,7 +2596,7 @@ mt7915_mcu_background_chain_ctrl(struct mt7915_phy *phy,
req.monitor_chan = chandef->chan->hw_value;
req.monitor_central_chan =
ieee80211_frequency_to_channel(chandef->center_freq1);
- req.band_idx = phy != &dev->phy;
+ req.band_idx = phy->mt76->band_idx;
req.scan_mode = 2;
break;
case CH_SWITCH_BACKGROUND_SCAN_STOP:
@@ -2613,12 +2663,13 @@ int mt7915_mcu_set_chan_info(struct mt7915_phy *phy, int cmd)
struct mt7915_dev *dev = phy->dev;
struct cfg80211_chan_def *chandef = &phy->mt76->chandef;
int freq1 = chandef->center_freq1;
+ u8 band = phy->mt76->band_idx;
struct {
u8 control_ch;
u8 center_ch;
u8 bw;
- u8 tx_streams_num;
- u8 rx_streams; /* mask or num */
+ u8 tx_path_num;
+ u8 rx_path; /* mask or num */
u8 switch_reason;
u8 band_idx;
u8 center_ch2; /* for 80+80 only */
@@ -2634,25 +2685,23 @@ int mt7915_mcu_set_chan_info(struct mt7915_phy *phy, int cmd)
.control_ch = chandef->chan->hw_value,
.center_ch = ieee80211_frequency_to_channel(freq1),
.bw = mt76_connac_chan_bw(chandef),
- .tx_streams_num = hweight8(phy->mt76->antenna_mask),
- .rx_streams = phy->mt76->antenna_mask,
- .band_idx = phy->band_idx,
+ .tx_path_num = hweight16(phy->mt76->chainmask),
+ .rx_path = phy->mt76->chainmask >> (dev->chainshift * band),
+ .band_idx = band,
.channel_band = ch_band[chandef->chan->band],
};
#ifdef CONFIG_NL80211_TESTMODE
if (phy->mt76->test.tx_antenna_mask &&
- (phy->mt76->test.state == MT76_TM_STATE_TX_FRAMES ||
- phy->mt76->test.state == MT76_TM_STATE_RX_FRAMES ||
- phy->mt76->test.state == MT76_TM_STATE_TX_CONT)) {
- req.tx_streams_num = fls(phy->mt76->test.tx_antenna_mask);
- req.rx_streams = phy->mt76->test.tx_antenna_mask;
-
- if (phy != &dev->phy)
- req.rx_streams >>= dev->chainshift;
+ mt76_testmode_enabled(phy->mt76)) {
+ req.tx_path_num = fls(phy->mt76->test.tx_antenna_mask);
+ req.rx_path = phy->mt76->test.tx_antenna_mask;
}
#endif
+ if (mt76_connac_spe_idx(phy->mt76->antenna_mask))
+ req.tx_path_num = fls(phy->mt76->antenna_mask);
+
if (cmd == MCU_EXT_CMD(SET_RX_PATH) ||
dev->mt76.hw->conf.flags & IEEE80211_CONF_MONITOR)
req.switch_reason = CH_SWITCH_NORMAL;
@@ -2665,7 +2714,7 @@ int mt7915_mcu_set_chan_info(struct mt7915_phy *phy, int cmd)
req.switch_reason = CH_SWITCH_NORMAL;
if (cmd == MCU_EXT_CMD(CHANNEL_SWITCH))
- req.rx_streams = hweight8(req.rx_streams);
+ req.rx_path = hweight8(req.rx_path);
if (chandef->width == NL80211_CHAN_WIDTH_80P80) {
int freq2 = chandef->center_freq2;
@@ -2927,25 +2976,36 @@ int mt7915_mcu_get_chan_mib_info(struct mt7915_phy *phy, bool chan_switch)
{
/* strict order */
static const u32 offs[] = {
- MIB_BUSY_TIME, MIB_TX_TIME, MIB_RX_TIME, MIB_OBSS_AIRTIME,
- MIB_BUSY_TIME_V2, MIB_TX_TIME_V2, MIB_RX_TIME_V2,
+ MIB_NON_WIFI_TIME,
+ MIB_TX_TIME,
+ MIB_RX_TIME,
+ MIB_OBSS_AIRTIME,
+ MIB_TXOP_INIT_COUNT,
+ /* v2 */
+ MIB_NON_WIFI_TIME_V2,
+ MIB_TX_TIME_V2,
+ MIB_RX_TIME_V2,
MIB_OBSS_AIRTIME_V2
};
struct mt76_channel_state *state = phy->mt76->chan_state;
struct mt76_channel_state *state_ts = &phy->state_ts;
struct mt7915_dev *dev = phy->dev;
- struct mt7915_mcu_mib *res, req[4];
+ struct mt7915_mcu_mib *res, req[5];
struct sk_buff *skb;
int i, ret, start = 0, ofs = 20;
+ u64 cc_tx;
if (!is_mt7915(&dev->mt76)) {
- start = 4;
+ start = 5;
ofs = 0;
}
- for (i = 0; i < 4; i++) {
- req[i].band = cpu_to_le32(phy != &dev->phy);
+ for (i = 0; i < 5; i++) {
+ req[i].band = cpu_to_le32(phy->mt76->band_idx);
req[i].offs = cpu_to_le32(offs[i + start]);
+
+ if (!is_mt7915(&dev->mt76) && i == 3)
+ break;
}
ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_EXT_CMD(GET_MIB_INFO),
@@ -2955,20 +3015,24 @@ int mt7915_mcu_get_chan_mib_info(struct mt7915_phy *phy, bool chan_switch)
res = (struct mt7915_mcu_mib *)(skb->data + ofs);
+#define __res_u64(s) le64_to_cpu(res[s].data)
+ /* subtract Tx backoff time from Tx duration */
+ cc_tx = is_mt7915(&dev->mt76) ? __res_u64(1) - __res_u64(4) : __res_u64(1);
+
if (chan_switch)
goto out;
-#define __res_u64(s) le64_to_cpu(res[s].data)
- state->cc_busy += __res_u64(0) - state_ts->cc_busy;
- state->cc_tx += __res_u64(1) - state_ts->cc_tx;
+ state->cc_tx += cc_tx - state_ts->cc_tx;
state->cc_bss_rx += __res_u64(2) - state_ts->cc_bss_rx;
state->cc_rx += __res_u64(2) + __res_u64(3) - state_ts->cc_rx;
+ state->cc_busy += __res_u64(0) + cc_tx + __res_u64(2) + __res_u64(3) -
+ state_ts->cc_busy;
out:
- state_ts->cc_busy = __res_u64(0);
- state_ts->cc_tx = __res_u64(1);
+ state_ts->cc_tx = cc_tx;
state_ts->cc_bss_rx = __res_u64(2);
state_ts->cc_rx = __res_u64(2) + __res_u64(3);
+ state_ts->cc_busy = __res_u64(0) + cc_tx + __res_u64(2) + __res_u64(3);
#undef __res_u64
dev_kfree_skb(skb);
@@ -2982,11 +3046,11 @@ int mt7915_mcu_get_temperature(struct mt7915_phy *phy)
struct {
u8 ctrl_id;
u8 action;
- u8 dbdc_idx;
+ u8 band_idx;
u8 rsv[5];
} req = {
.ctrl_id = THERMAL_SENSOR_TEMP_QUERY,
- .dbdc_idx = phy != &dev->phy,
+ .band_idx = phy->mt76->band_idx,
};
return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(THERMAL_CTRL), &req,
@@ -3005,7 +3069,7 @@ int mt7915_mcu_set_thermal_throttling(struct mt7915_phy *phy, u8 state)
u8 rsv[2];
} __packed req = {
.ctrl = {
- .band_idx = phy->band_idx,
+ .band_idx = phy->mt76->band_idx,
},
};
int level;
@@ -3045,28 +3109,103 @@ out:
&req, sizeof(req), false);
}
-int mt7915_mcu_set_txpower_sku(struct mt7915_phy *phy)
+int mt7915_mcu_set_txpower_frame_min(struct mt7915_phy *phy, s8 txpower)
{
struct mt7915_dev *dev = phy->dev;
+ struct {
+ u8 format_id;
+ u8 rsv;
+ u8 band_idx;
+ s8 txpower_min;
+ } __packed req = {
+ .format_id = TX_POWER_LIMIT_FRAME_MIN,
+ .band_idx = phy->mt76->band_idx,
+ .txpower_min = txpower * 2, /* 0.5db */
+ };
+
+ return mt76_mcu_send_msg(&dev->mt76,
+ MCU_EXT_CMD(TX_POWER_FEATURE_CTRL), &req,
+ sizeof(req), true);
+}
+
+int mt7915_mcu_set_txpower_frame(struct mt7915_phy *phy,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, s8 txpower)
+{
+ struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv;
+ struct mt7915_dev *dev = phy->dev;
struct mt76_phy *mphy = phy->mt76;
- struct ieee80211_hw *hw = mphy->hw;
- struct mt7915_sku_val {
+ struct {
u8 format_id;
- u8 limit_type;
- u8 dbdc_idx;
- s8 val[MT7915_SKU_RATE_NUM];
+ u8 rsv[3];
+ u8 band_idx;
+ s8 txpower_max;
+ __le16 wcid;
+ s8 txpower_offs[48];
} __packed req = {
- .format_id = 4,
- .dbdc_idx = phy != &dev->phy,
+ .format_id = TX_POWER_LIMIT_FRAME,
+ .band_idx = phy->mt76->band_idx,
+ .txpower_max = DIV_ROUND_UP(mphy->txpower_cur, 2),
+ .wcid = cpu_to_le16(msta->wcid.idx),
+ };
+ int ret;
+ s8 txpower_sku[MT7915_SKU_RATE_NUM];
+
+ ret = mt7915_mcu_get_txpower_sku(phy, txpower_sku, sizeof(txpower_sku));
+ if (ret)
+ return ret;
+
+ txpower = mt7915_get_power_bound(phy, txpower);
+ if (txpower > mphy->txpower_cur || txpower < 0)
+ return -EINVAL;
+
+ if (txpower) {
+ u32 offs, len, i;
+
+ if (sta->deflink.ht_cap.ht_supported) {
+ const u8 *sku_len = mt7915_sku_group_len;
+
+ offs = sku_len[SKU_CCK] + sku_len[SKU_OFDM];
+ len = sku_len[SKU_HT_BW20] + sku_len[SKU_HT_BW40];
+
+ if (sta->deflink.vht_cap.vht_supported) {
+ offs += len;
+ len = sku_len[SKU_VHT_BW20] * 4;
+
+ if (sta->deflink.he_cap.has_he) {
+ offs += len + sku_len[SKU_HE_RU26] * 3;
+ len = sku_len[SKU_HE_RU242] * 4;
+ }
+ }
+ } else {
+ return -EINVAL;
+ }
+
+ for (i = 0; i < len; i++, offs++)
+ req.txpower_offs[i] =
+ DIV_ROUND_UP(txpower - txpower_sku[offs], 2);
+ }
+
+ return mt76_mcu_send_msg(&dev->mt76,
+ MCU_EXT_CMD(TX_POWER_FEATURE_CTRL), &req,
+ sizeof(req), true);
+}
+
+int mt7915_mcu_set_txpower_sku(struct mt7915_phy *phy)
+{
+ struct mt7915_dev *dev = phy->dev;
+ struct mt76_phy *mphy = phy->mt76;
+ struct ieee80211_hw *hw = mphy->hw;
+ struct mt7915_mcu_txpower_sku req = {
+ .format_id = TX_POWER_LIMIT_TABLE,
+ .band_idx = phy->mt76->band_idx,
};
struct mt76_power_limits limits_array;
s8 *la = (s8 *)&limits_array;
- int i, idx, n_chains = hweight8(mphy->antenna_mask);
- int tx_power = hw->conf.power_level * 2;
+ int i, idx;
+ int tx_power;
- tx_power = mt76_get_sar_power(mphy, mphy->chandef.chan,
- tx_power);
- tx_power -= mt76_tx_power_nss_delta(n_chains);
+ tx_power = mt7915_get_power_bound(phy, hw->conf.power_level);
tx_power = mt76_get_rate_power_limits(mphy, mphy->chandef.chan,
&limits_array, tx_power);
mphy->txpower_cur = tx_power;
@@ -3085,7 +3224,7 @@ int mt7915_mcu_set_txpower_sku(struct mt7915_phy *phy)
}
for (j = 0; j < min_t(u8, mcs_num, len); j++)
- req.val[idx + j] = la[j];
+ req.txpower_sku[idx + j] = la[j];
la += mcs_num;
idx += len;
@@ -3103,14 +3242,14 @@ int mt7915_mcu_get_txpower_sku(struct mt7915_phy *phy, s8 *txpower, int len)
struct {
u8 format_id;
u8 category;
- u8 band;
+ u8 band_idx;
u8 _rsv;
} __packed req = {
- .format_id = 7,
+ .format_id = TX_POWER_LIMIT_INFO,
.category = RATE_POWER_INFO,
- .band = phy != &dev->phy,
+ .band_idx = phy->mt76->band_idx,
};
- s8 res[MT7915_SKU_RATE_NUM][2];
+ s8 txpower_sku[MT7915_SKU_RATE_NUM][2];
struct sk_buff *skb;
int ret, i;
@@ -3120,9 +3259,9 @@ int mt7915_mcu_get_txpower_sku(struct mt7915_phy *phy, s8 *txpower, int len)
if (ret)
return ret;
- memcpy(res, skb->data + 4, sizeof(res));
+ memcpy(txpower_sku, skb->data + 4, sizeof(txpower_sku));
for (i = 0; i < len; i++)
- txpower[i] = res[i][req.band];
+ txpower[i] = txpower_sku[i][req.band_idx];
dev_kfree_skb(skb);
@@ -3157,11 +3296,11 @@ int mt7915_mcu_set_sku_en(struct mt7915_phy *phy, bool enable)
struct mt7915_sku {
u8 format_id;
u8 sku_enable;
- u8 dbdc_idx;
+ u8 band_idx;
u8 rsv;
} __packed req = {
- .format_id = 0,
- .dbdc_idx = phy != &dev->phy,
+ .format_id = TX_POWER_LIMIT_ENABLE,
+ .band_idx = phy->mt76->band_idx,
.sku_enable = enable,
};
@@ -3236,31 +3375,193 @@ int mt7915_mcu_set_txbf(struct mt7915_dev *dev, u8 action)
sizeof(req), true);
}
-int mt7915_mcu_add_obss_spr(struct mt7915_dev *dev, struct ieee80211_vif *vif,
- bool enable)
+static int
+mt7915_mcu_enable_obss_spr(struct mt7915_phy *phy, u8 action, u8 val)
+{
+ struct mt7915_dev *dev = phy->dev;
+ struct mt7915_mcu_sr_ctrl req = {
+ .action = action,
+ .argnum = 1,
+ .band_idx = phy->mt76->band_idx,
+ .val = cpu_to_le32(val),
+ };
+
+ return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(SET_SPR), &req,
+ sizeof(req), true);
+}
+
+static int
+mt7915_mcu_set_obss_spr_pd(struct mt7915_phy *phy,
+ struct ieee80211_he_obss_pd *he_obss_pd)
+{
+ struct mt7915_dev *dev = phy->dev;
+ struct {
+ struct mt7915_mcu_sr_ctrl ctrl;
+ struct {
+ u8 pd_th_non_srg;
+ u8 pd_th_srg;
+ u8 period_offs;
+ u8 rcpi_src;
+ __le16 obss_pd_min;
+ __le16 obss_pd_min_srg;
+ u8 resp_txpwr_mode;
+ u8 txpwr_restrict_mode;
+ u8 txpwr_ref;
+ u8 rsv[3];
+ } __packed param;
+ } __packed req = {
+ .ctrl = {
+ .action = SPR_SET_PARAM,
+ .argnum = 9,
+ .band_idx = phy->mt76->band_idx,
+ },
+ };
+ int ret;
+ u8 max_th = 82, non_srg_max_th = 62;
+
+ /* disable firmware dynamical PD asjustment */
+ ret = mt7915_mcu_enable_obss_spr(phy, SPR_ENABLE_DPD, false);
+ if (ret)
+ return ret;
+
+ if (he_obss_pd->sr_ctrl &
+ IEEE80211_HE_SPR_NON_SRG_OBSS_PD_SR_DISALLOWED)
+ req.param.pd_th_non_srg = max_th;
+ else if (he_obss_pd->sr_ctrl & IEEE80211_HE_SPR_NON_SRG_OFFSET_PRESENT)
+ req.param.pd_th_non_srg = max_th - he_obss_pd->non_srg_max_offset;
+ else
+ req.param.pd_th_non_srg = non_srg_max_th;
+
+ if (he_obss_pd->sr_ctrl & IEEE80211_HE_SPR_SRG_INFORMATION_PRESENT)
+ req.param.pd_th_srg = max_th - he_obss_pd->max_offset;
+
+ req.param.obss_pd_min = cpu_to_le16(82);
+ req.param.obss_pd_min_srg = cpu_to_le16(82);
+ req.param.txpwr_restrict_mode = 2;
+ req.param.txpwr_ref = 21;
+
+ return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(SET_SPR), &req,
+ sizeof(req), true);
+}
+
+static int
+mt7915_mcu_set_obss_spr_siga(struct mt7915_phy *phy, struct ieee80211_vif *vif,
+ struct ieee80211_he_obss_pd *he_obss_pd)
{
-#define MT_SPR_ENABLE 1
struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv;
+ struct mt7915_dev *dev = phy->dev;
+ u8 omac = mvif->mt76.omac_idx;
struct {
- u8 action;
- u8 arg_num;
- u8 band_idx;
- u8 status;
- u8 drop_tx_idx;
- u8 sta_idx; /* 256 sta */
- u8 rsv[2];
- __le32 val;
+ struct mt7915_mcu_sr_ctrl ctrl;
+ struct {
+ u8 omac;
+ u8 rsv[3];
+ u8 flag[20];
+ } __packed siga;
+ } __packed req = {
+ .ctrl = {
+ .action = SPR_SET_SIGA,
+ .argnum = 1,
+ .band_idx = phy->mt76->band_idx,
+ },
+ .siga = {
+ .omac = omac > HW_BSSID_MAX ? omac - 12 : omac,
+ },
+ };
+ int ret;
+
+ if (he_obss_pd->sr_ctrl & IEEE80211_HE_SPR_HESIGA_SR_VAL15_ALLOWED)
+ req.siga.flag[req.siga.omac] = 0xf;
+ else
+ return 0;
+
+ /* switch to normal AP mode */
+ ret = mt7915_mcu_enable_obss_spr(phy, SPR_ENABLE_MODE, 0);
+ if (ret)
+ return ret;
+
+ return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(SET_SPR), &req,
+ sizeof(req), true);
+}
+
+static int
+mt7915_mcu_set_obss_spr_bitmap(struct mt7915_phy *phy,
+ struct ieee80211_he_obss_pd *he_obss_pd)
+{
+ struct mt7915_dev *dev = phy->dev;
+ struct {
+ struct mt7915_mcu_sr_ctrl ctrl;
+ struct {
+ __le32 color_l[2];
+ __le32 color_h[2];
+ __le32 bssid_l[2];
+ __le32 bssid_h[2];
+ } __packed bitmap;
} __packed req = {
- .action = MT_SPR_ENABLE,
- .arg_num = 1,
- .band_idx = mvif->mt76.band_idx,
- .val = cpu_to_le32(enable),
+ .ctrl = {
+ .action = SPR_SET_SRG_BITMAP,
+ .argnum = 4,
+ .band_idx = phy->mt76->band_idx,
+ },
};
+ u32 bitmap;
+
+ memcpy(&bitmap, he_obss_pd->bss_color_bitmap, sizeof(bitmap));
+ req.bitmap.color_l[req.ctrl.band_idx] = cpu_to_le32(bitmap);
+
+ memcpy(&bitmap, he_obss_pd->bss_color_bitmap + 4, sizeof(bitmap));
+ req.bitmap.color_h[req.ctrl.band_idx] = cpu_to_le32(bitmap);
+
+ memcpy(&bitmap, he_obss_pd->partial_bssid_bitmap, sizeof(bitmap));
+ req.bitmap.bssid_l[req.ctrl.band_idx] = cpu_to_le32(bitmap);
+
+ memcpy(&bitmap, he_obss_pd->partial_bssid_bitmap + 4, sizeof(bitmap));
+ req.bitmap.bssid_h[req.ctrl.band_idx] = cpu_to_le32(bitmap);
return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(SET_SPR), &req,
sizeof(req), true);
}
+int mt7915_mcu_add_obss_spr(struct mt7915_phy *phy, struct ieee80211_vif *vif,
+ struct ieee80211_he_obss_pd *he_obss_pd)
+{
+ int ret;
+
+ /* enable firmware scene detection algorithms */
+ ret = mt7915_mcu_enable_obss_spr(phy, SPR_ENABLE_SD, sr_scene_detect);
+ if (ret)
+ return ret;
+
+ /* firmware dynamically adjusts PD threshold so skip manual control */
+ if (sr_scene_detect && !he_obss_pd->enable)
+ return 0;
+
+ /* enable spatial reuse */
+ ret = mt7915_mcu_enable_obss_spr(phy, SPR_ENABLE, he_obss_pd->enable);
+ if (ret)
+ return ret;
+
+ if (sr_scene_detect || !he_obss_pd->enable)
+ return 0;
+
+ ret = mt7915_mcu_enable_obss_spr(phy, SPR_ENABLE_TX, true);
+ if (ret)
+ return ret;
+
+ /* set SRG/non-SRG OBSS PD threshold */
+ ret = mt7915_mcu_set_obss_spr_pd(phy, he_obss_pd);
+ if (ret)
+ return ret;
+
+ /* Set SR prohibit */
+ ret = mt7915_mcu_set_obss_spr_siga(phy, vif, he_obss_pd);
+ if (ret)
+ return ret;
+
+ /* set SRG BSS color/BSSID bitmap */
+ return mt7915_mcu_set_obss_spr_bitmap(phy, he_obss_pd);
+}
+
int mt7915_mcu_get_rx_rate(struct mt7915_phy *phy, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, struct rate_info *rate)
{
@@ -3447,8 +3748,8 @@ int mt7915_mcu_rf_regval(struct mt7915_dev *dev, u32 regidx, u32 *val, bool set)
__le32 ofs;
__le32 data;
} __packed req = {
- .idx = cpu_to_le32(u32_get_bits(regidx, GENMASK(31, 28))),
- .ofs = cpu_to_le32(u32_get_bits(regidx, GENMASK(27, 0))),
+ .idx = cpu_to_le32(u32_get_bits(regidx, GENMASK(31, 24))),
+ .ofs = cpu_to_le32(u32_get_bits(regidx, GENMASK(23, 0))),
.data = set ? cpu_to_le32(*val) : 0,
};
struct sk_buff *skb;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h
index cd1edf553fc1..29b5434bfdb8 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h
@@ -129,6 +129,17 @@ struct mt7915_mcu_background_chain_ctrl {
u8 rsv[2];
} __packed;
+struct mt7915_mcu_sr_ctrl {
+ u8 action;
+ u8 argnum;
+ u8 band_idx;
+ u8 status;
+ u8 drop_ta_idx;
+ u8 sta_idx; /* 256 sta */
+ u8 rsv[2];
+ __le32 val;
+} __packed;
+
struct mt7915_mcu_eeprom {
u8 buffer_mode;
u8 format;
@@ -160,17 +171,26 @@ struct mt7915_mcu_mib {
enum mt7915_chan_mib_offs {
/* mt7915 */
- MIB_BUSY_TIME = 14,
MIB_TX_TIME = 81,
MIB_RX_TIME,
MIB_OBSS_AIRTIME = 86,
+ MIB_NON_WIFI_TIME,
+ MIB_TXOP_INIT_COUNT,
+
/* mt7916 */
- MIB_BUSY_TIME_V2 = 0,
MIB_TX_TIME_V2 = 6,
MIB_RX_TIME_V2 = 8,
- MIB_OBSS_AIRTIME_V2 = 490
+ MIB_OBSS_AIRTIME_V2 = 490,
+ MIB_NON_WIFI_TIME_V2
};
+struct mt7915_mcu_txpower_sku {
+ u8 format_id;
+ u8 limit_type;
+ u8 band_idx;
+ s8 txpower_sku[MT7915_SKU_RATE_NUM];
+} __packed;
+
struct edca {
u8 queue;
u8 set;
@@ -394,6 +414,7 @@ enum {
RATE_PARAM_FIXED_MCS,
RATE_PARAM_FIXED_GI = 11,
RATE_PARAM_AUTO = 20,
+ RATE_PARAM_SPE_UPDATE = 22,
};
#define RATE_CFG_MCS GENMASK(3, 0)
@@ -406,6 +427,25 @@ enum {
#define RATE_CFG_HE_LTF GENMASK(31, 28)
enum {
+ TX_POWER_LIMIT_ENABLE,
+ TX_POWER_LIMIT_TABLE = 0x4,
+ TX_POWER_LIMIT_INFO = 0x7,
+ TX_POWER_LIMIT_FRAME = 0x11,
+ TX_POWER_LIMIT_FRAME_MIN = 0x12,
+};
+
+enum {
+ SPR_ENABLE = 0x1,
+ SPR_ENABLE_SD = 0x3,
+ SPR_ENABLE_MODE = 0x5,
+ SPR_ENABLE_DPD = 0x23,
+ SPR_ENABLE_TX = 0x25,
+ SPR_SET_SRG_BITMAP = 0x80,
+ SPR_SET_PARAM = 0xc2,
+ SPR_SET_SIGA = 0xdc,
+};
+
+enum {
THERMAL_PROTECT_PARAMETER_CTRL,
THERMAL_PROTECT_BASIC_INFO,
THERMAL_PROTECT_ENABLE,
@@ -447,6 +487,8 @@ enum {
SER_SET_RECOVER_L3_TX_ABORT,
SER_SET_RECOVER_L3_TX_DISABLE,
SER_SET_RECOVER_L3_BF,
+ SER_SET_RECOVER_FULL,
+ SER_SET_SYSTEM_ASSERT,
/* action */
SER_ENABLE = 2,
SER_RECOVER
@@ -474,4 +516,16 @@ enum {
sizeof(struct bss_info_bcn_cont) + \
sizeof(struct bss_info_inband_discovery))
+static inline s8
+mt7915_get_power_bound(struct mt7915_phy *phy, s8 txpower)
+{
+ struct mt76_phy *mphy = phy->mt76;
+ int n_chains = hweight8(mphy->antenna_mask);
+
+ 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 7bd5f6725d7b..0a95c3da241b 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c
@@ -9,53 +9,112 @@
#include "mt7915.h"
#include "mac.h"
#include "../trace.h"
+#include "../dma.h"
+
+static bool wed_enable;
+module_param(wed_enable, bool, 0644);
+MODULE_PARM_DESC(wed_enable, "Enable Wireless Ethernet Dispatch support");
static const u32 mt7915_reg[] = {
- [INT_SOURCE_CSR] = 0xd7010,
- [INT_MASK_CSR] = 0xd7014,
- [INT1_SOURCE_CSR] = 0xd7088,
- [INT1_MASK_CSR] = 0xd708c,
- [INT_MCU_CMD_SOURCE] = 0xd51f0,
- [INT_MCU_CMD_EVENT] = 0x3108,
- [WFDMA0_ADDR] = 0xd4000,
- [WFDMA0_PCIE1_ADDR] = 0xd8000,
- [WFDMA_EXT_CSR_ADDR] = 0xd7000,
- [CBTOP1_PHY_END] = 0x77ffffff,
- [INFRA_MCU_ADDR_END] = 0x7c3fffff,
- [FW_EXCEPTION_ADDR] = 0x219848,
- [SWDEF_BASE_ADDR] = 0x41f200,
+ [INT_SOURCE_CSR] = 0xd7010,
+ [INT_MASK_CSR] = 0xd7014,
+ [INT1_SOURCE_CSR] = 0xd7088,
+ [INT1_MASK_CSR] = 0xd708c,
+ [INT_MCU_CMD_SOURCE] = 0xd51f0,
+ [INT_MCU_CMD_EVENT] = 0x3108,
+ [WFDMA0_ADDR] = 0xd4000,
+ [WFDMA0_PCIE1_ADDR] = 0xd8000,
+ [WFDMA_EXT_CSR_ADDR] = 0xd7000,
+ [CBTOP1_PHY_END] = 0x77ffffff,
+ [INFRA_MCU_ADDR_END] = 0x7c3fffff,
+ [FW_ASSERT_STAT_ADDR] = 0x219848,
+ [FW_EXCEPT_TYPE_ADDR] = 0x21987c,
+ [FW_EXCEPT_COUNT_ADDR] = 0x219848,
+ [FW_CIRQ_COUNT_ADDR] = 0x216f94,
+ [FW_CIRQ_IDX_ADDR] = 0x216ef8,
+ [FW_CIRQ_LISR_ADDR] = 0x2170ac,
+ [FW_TASK_ID_ADDR] = 0x216f90,
+ [FW_TASK_IDX_ADDR] = 0x216f9c,
+ [FW_TASK_QID1_ADDR] = 0x219680,
+ [FW_TASK_QID2_ADDR] = 0x219760,
+ [FW_TASK_START_ADDR] = 0x219558,
+ [FW_TASK_END_ADDR] = 0x219554,
+ [FW_TASK_SIZE_ADDR] = 0x219560,
+ [FW_LAST_MSG_ID_ADDR] = 0x216f70,
+ [FW_EINT_INFO_ADDR] = 0x219818,
+ [FW_SCHED_INFO_ADDR] = 0x219828,
+ [SWDEF_BASE_ADDR] = 0x41f200,
+ [TXQ_WED_RING_BASE] = 0xd7300,
+ [RXQ_WED_RING_BASE] = 0xd7410,
+ [RXQ_WED_DATA_RING_BASE] = 0xd4500,
};
static const u32 mt7916_reg[] = {
- [INT_SOURCE_CSR] = 0xd4200,
- [INT_MASK_CSR] = 0xd4204,
- [INT1_SOURCE_CSR] = 0xd8200,
- [INT1_MASK_CSR] = 0xd8204,
- [INT_MCU_CMD_SOURCE] = 0xd41f0,
- [INT_MCU_CMD_EVENT] = 0x2108,
- [WFDMA0_ADDR] = 0xd4000,
- [WFDMA0_PCIE1_ADDR] = 0xd8000,
- [WFDMA_EXT_CSR_ADDR] = 0xd7000,
- [CBTOP1_PHY_END] = 0x7fffffff,
- [INFRA_MCU_ADDR_END] = 0x7c085fff,
- [FW_EXCEPTION_ADDR] = 0x022050bc,
- [SWDEF_BASE_ADDR] = 0x411400,
+ [INT_SOURCE_CSR] = 0xd4200,
+ [INT_MASK_CSR] = 0xd4204,
+ [INT1_SOURCE_CSR] = 0xd8200,
+ [INT1_MASK_CSR] = 0xd8204,
+ [INT_MCU_CMD_SOURCE] = 0xd41f0,
+ [INT_MCU_CMD_EVENT] = 0x2108,
+ [WFDMA0_ADDR] = 0xd4000,
+ [WFDMA0_PCIE1_ADDR] = 0xd8000,
+ [WFDMA_EXT_CSR_ADDR] = 0xd7000,
+ [CBTOP1_PHY_END] = 0x7fffffff,
+ [INFRA_MCU_ADDR_END] = 0x7c085fff,
+ [FW_ASSERT_STAT_ADDR] = 0x02204c14,
+ [FW_EXCEPT_TYPE_ADDR] = 0x022051a4,
+ [FW_EXCEPT_COUNT_ADDR] = 0x022050bc,
+ [FW_CIRQ_COUNT_ADDR] = 0x022001ac,
+ [FW_CIRQ_IDX_ADDR] = 0x02204f84,
+ [FW_CIRQ_LISR_ADDR] = 0x022050d0,
+ [FW_TASK_ID_ADDR] = 0x0220406c,
+ [FW_TASK_IDX_ADDR] = 0x0220500c,
+ [FW_TASK_QID1_ADDR] = 0x022028c8,
+ [FW_TASK_QID2_ADDR] = 0x02202a38,
+ [FW_TASK_START_ADDR] = 0x0220286c,
+ [FW_TASK_END_ADDR] = 0x02202870,
+ [FW_TASK_SIZE_ADDR] = 0x02202878,
+ [FW_LAST_MSG_ID_ADDR] = 0x02204fe8,
+ [FW_EINT_INFO_ADDR] = 0x0220525c,
+ [FW_SCHED_INFO_ADDR] = 0x0220516c,
+ [SWDEF_BASE_ADDR] = 0x411400,
+ [TXQ_WED_RING_BASE] = 0xd7300,
+ [RXQ_WED_RING_BASE] = 0xd7410,
+ [RXQ_WED_DATA_RING_BASE] = 0xd4540,
};
static const u32 mt7986_reg[] = {
- [INT_SOURCE_CSR] = 0x24200,
- [INT_MASK_CSR] = 0x24204,
- [INT1_SOURCE_CSR] = 0x28200,
- [INT1_MASK_CSR] = 0x28204,
- [INT_MCU_CMD_SOURCE] = 0x241f0,
- [INT_MCU_CMD_EVENT] = 0x54000108,
- [WFDMA0_ADDR] = 0x24000,
- [WFDMA0_PCIE1_ADDR] = 0x28000,
- [WFDMA_EXT_CSR_ADDR] = 0x27000,
- [CBTOP1_PHY_END] = 0x7fffffff,
- [INFRA_MCU_ADDR_END] = 0x7c085fff,
- [FW_EXCEPTION_ADDR] = 0x02204ffc,
- [SWDEF_BASE_ADDR] = 0x411400,
+ [INT_SOURCE_CSR] = 0x24200,
+ [INT_MASK_CSR] = 0x24204,
+ [INT1_SOURCE_CSR] = 0x28200,
+ [INT1_MASK_CSR] = 0x28204,
+ [INT_MCU_CMD_SOURCE] = 0x241f0,
+ [INT_MCU_CMD_EVENT] = 0x54000108,
+ [WFDMA0_ADDR] = 0x24000,
+ [WFDMA0_PCIE1_ADDR] = 0x28000,
+ [WFDMA_EXT_CSR_ADDR] = 0x27000,
+ [CBTOP1_PHY_END] = 0x7fffffff,
+ [INFRA_MCU_ADDR_END] = 0x7c085fff,
+ [FW_ASSERT_STAT_ADDR] = 0x02204b54,
+ [FW_EXCEPT_TYPE_ADDR] = 0x022050dc,
+ [FW_EXCEPT_COUNT_ADDR] = 0x02204ffc,
+ [FW_CIRQ_COUNT_ADDR] = 0x022001ac,
+ [FW_CIRQ_IDX_ADDR] = 0x02204ec4,
+ [FW_CIRQ_LISR_ADDR] = 0x02205010,
+ [FW_TASK_ID_ADDR] = 0x02204fac,
+ [FW_TASK_IDX_ADDR] = 0x02204f4c,
+ [FW_TASK_QID1_ADDR] = 0x02202814,
+ [FW_TASK_QID2_ADDR] = 0x02202984,
+ [FW_TASK_START_ADDR] = 0x022027b8,
+ [FW_TASK_END_ADDR] = 0x022027bc,
+ [FW_TASK_SIZE_ADDR] = 0x022027c4,
+ [FW_LAST_MSG_ID_ADDR] = 0x02204f28,
+ [FW_EINT_INFO_ADDR] = 0x02205194,
+ [FW_SCHED_INFO_ADDR] = 0x022051a4,
+ [SWDEF_BASE_ADDR] = 0x411400,
+ [TXQ_WED_RING_BASE] = 0x24420,
+ [RXQ_WED_RING_BASE] = 0x24520,
+ [RXQ_WED_DATA_RING_BASE] = 0x24540,
};
static const u32 mt7915_offs[] = {
@@ -448,6 +507,14 @@ static u32 __mt7915_reg_addr(struct mt7915_dev *dev, u32 addr)
return mt7915_reg_map_l2(dev, addr);
}
+void mt7915_memcpy_fromio(struct mt7915_dev *dev, void *buf, u32 offset,
+ size_t len)
+{
+ u32 addr = __mt7915_reg_addr(dev, offset);
+
+ memcpy_fromio(buf, dev->mt76.mmio.regs + addr, len);
+}
+
static u32 mt7915_rr(struct mt76_dev *mdev, u32 offset)
{
struct mt7915_dev *dev = container_of(mdev, struct mt7915_dev, mt76);
@@ -472,6 +539,257 @@ static u32 mt7915_rmw(struct mt76_dev *mdev, u32 offset, u32 mask, u32 val)
return dev->bus_ops->rmw(mdev, addr, mask, val);
}
+#ifdef CONFIG_NET_MEDIATEK_SOC_WED
+static int mt7915_mmio_wed_offload_enable(struct mtk_wed_device *wed)
+{
+ struct mt7915_dev *dev;
+ struct mt7915_phy *phy;
+ int ret;
+
+ dev = container_of(wed, struct mt7915_dev, mt76.mmio.wed);
+
+ spin_lock_bh(&dev->mt76.token_lock);
+ dev->mt76.token_size = wed->wlan.token_start;
+ spin_unlock_bh(&dev->mt76.token_lock);
+
+ ret = wait_event_timeout(dev->mt76.tx_wait,
+ !dev->mt76.wed_token_count, HZ);
+ if (!ret)
+ return -EAGAIN;
+
+ phy = &dev->phy;
+ mt76_set(dev, MT_AGG_ACR4(phy->mt76->band_idx), MT_AGG_ACR_PPDU_TXS2H);
+
+ phy = dev->mt76.phys[MT_BAND1] ? dev->mt76.phys[MT_BAND1]->priv : NULL;
+ if (phy)
+ mt76_set(dev, MT_AGG_ACR4(phy->mt76->band_idx),
+ MT_AGG_ACR_PPDU_TXS2H);
+
+ return 0;
+}
+
+static void mt7915_mmio_wed_offload_disable(struct mtk_wed_device *wed)
+{
+ struct mt7915_dev *dev;
+ struct mt7915_phy *phy;
+
+ dev = container_of(wed, struct mt7915_dev, mt76.mmio.wed);
+
+ spin_lock_bh(&dev->mt76.token_lock);
+ dev->mt76.token_size = MT7915_TOKEN_SIZE;
+ spin_unlock_bh(&dev->mt76.token_lock);
+
+ /* MT_TXD5_TX_STATUS_HOST (MPDU format) has higher priority than
+ * MT_AGG_ACR_PPDU_TXS2H (PPDU format) even though ACR bit is set.
+ */
+ phy = &dev->phy;
+ mt76_clear(dev, MT_AGG_ACR4(phy->mt76->band_idx), MT_AGG_ACR_PPDU_TXS2H);
+
+ phy = dev->mt76.phys[MT_BAND1] ? dev->mt76.phys[MT_BAND1]->priv : NULL;
+ if (phy)
+ mt76_clear(dev, MT_AGG_ACR4(phy->mt76->band_idx),
+ MT_AGG_ACR_PPDU_TXS2H);
+}
+
+static void mt7915_mmio_wed_release_rx_buf(struct mtk_wed_device *wed)
+{
+ struct mt7915_dev *dev;
+ struct page *page;
+ int i;
+
+ dev = container_of(wed, struct mt7915_dev, mt76.mmio.wed);
+ for (i = 0; i < dev->mt76.rx_token_size; i++) {
+ struct mt76_txwi_cache *t;
+
+ t = mt76_rx_token_release(&dev->mt76, i);
+ if (!t || !t->ptr)
+ continue;
+
+ dma_unmap_single(dev->mt76.dma_dev, t->dma_addr,
+ wed->wlan.rx_size, DMA_FROM_DEVICE);
+ skb_free_frag(t->ptr);
+ t->ptr = NULL;
+
+ mt76_put_rxwi(&dev->mt76, t);
+ }
+
+ if (!wed->rx_buf_ring.rx_page.va)
+ return;
+
+ page = virt_to_page(wed->rx_buf_ring.rx_page.va);
+ __page_frag_cache_drain(page, wed->rx_buf_ring.rx_page.pagecnt_bias);
+ memset(&wed->rx_buf_ring.rx_page, 0, sizeof(wed->rx_buf_ring.rx_page));
+}
+
+static u32 mt7915_mmio_wed_init_rx_buf(struct mtk_wed_device *wed, int size)
+{
+ struct mtk_rxbm_desc *desc = wed->rx_buf_ring.desc;
+ struct mt7915_dev *dev;
+ u32 length;
+ int i;
+
+ dev = container_of(wed, struct mt7915_dev, mt76.mmio.wed);
+ length = SKB_DATA_ALIGN(NET_SKB_PAD + wed->wlan.rx_size +
+ sizeof(struct skb_shared_info));
+
+ for (i = 0; i < size; i++) {
+ struct mt76_txwi_cache *t = mt76_get_rxwi(&dev->mt76);
+ dma_addr_t phy_addr;
+ int token;
+ void *ptr;
+
+ ptr = page_frag_alloc(&wed->rx_buf_ring.rx_page, length,
+ GFP_KERNEL);
+ if (!ptr)
+ goto unmap;
+
+ phy_addr = dma_map_single(dev->mt76.dma_dev, ptr,
+ wed->wlan.rx_size,
+ DMA_TO_DEVICE);
+ if (unlikely(dma_mapping_error(dev->mt76.dev, phy_addr))) {
+ skb_free_frag(ptr);
+ goto unmap;
+ }
+
+ desc->buf0 = cpu_to_le32(phy_addr);
+ token = mt76_rx_token_consume(&dev->mt76, ptr, t, phy_addr);
+ desc->token |= cpu_to_le32(FIELD_PREP(MT_DMA_CTL_TOKEN,
+ token));
+ desc++;
+ }
+
+ return 0;
+
+unmap:
+ mt7915_mmio_wed_release_rx_buf(wed);
+ return -ENOMEM;
+}
+
+static void mt7915_mmio_wed_update_rx_stats(struct mtk_wed_device *wed,
+ struct mtk_wed_wo_rx_stats *stats)
+{
+ int idx = le16_to_cpu(stats->wlan_idx);
+ struct mt7915_dev *dev;
+ struct mt76_wcid *wcid;
+
+ dev = container_of(wed, struct mt7915_dev, mt76.mmio.wed);
+
+ if (idx >= mt7915_wtbl_size(dev))
+ return;
+
+ rcu_read_lock();
+
+ wcid = rcu_dereference(dev->mt76.wcid[idx]);
+ if (wcid) {
+ wcid->stats.rx_bytes += le32_to_cpu(stats->rx_byte_cnt);
+ wcid->stats.rx_packets += le32_to_cpu(stats->rx_pkt_cnt);
+ wcid->stats.rx_errors += le32_to_cpu(stats->rx_err_cnt);
+ wcid->stats.rx_drops += le32_to_cpu(stats->rx_drop_cnt);
+ }
+
+ rcu_read_unlock();
+}
+#endif
+
+int mt7915_mmio_wed_init(struct mt7915_dev *dev, void *pdev_ptr,
+ bool pci, int *irq)
+{
+#ifdef CONFIG_NET_MEDIATEK_SOC_WED
+ struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
+ int ret;
+
+ if (!wed_enable)
+ return 0;
+
+ if (pci) {
+ struct pci_dev *pci_dev = pdev_ptr;
+
+ wed->wlan.pci_dev = pci_dev;
+ wed->wlan.bus_type = MTK_WED_BUS_PCIE;
+ wed->wlan.base = devm_ioremap(dev->mt76.dev,
+ pci_resource_start(pci_dev, 0),
+ pci_resource_len(pci_dev, 0));
+ 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;
+ wed->wlan.wpdma_mask = pci_resource_start(pci_dev, 0) +
+ MT_INT_WED_MASK_CSR;
+ wed->wlan.wpdma_phys = pci_resource_start(pci_dev, 0) +
+ MT_WFDMA_EXT_CSR_BASE;
+ wed->wlan.wpdma_tx = pci_resource_start(pci_dev, 0) +
+ MT_TXQ_WED_RING_BASE;
+ wed->wlan.wpdma_txfree = pci_resource_start(pci_dev, 0) +
+ MT_RXQ_WED_RING_BASE;
+ wed->wlan.wpdma_rx_glo = pci_resource_start(pci_dev, 0) +
+ MT_WPDMA_GLO_CFG;
+ wed->wlan.wpdma_rx = pci_resource_start(pci_dev, 0) +
+ MT_RXQ_WED_DATA_RING_BASE;
+ } else {
+ struct platform_device *plat_dev = pdev_ptr;
+ struct resource *res;
+
+ res = platform_get_resource(plat_dev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENOMEM;
+
+ wed->wlan.platform_dev = plat_dev;
+ wed->wlan.bus_type = MTK_WED_BUS_AXI;
+ wed->wlan.base = devm_ioremap(dev->mt76.dev, res->start,
+ resource_size(res));
+ 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;
+ wed->wlan.wpdma_tx = res->start + MT_TXQ_WED_RING_BASE;
+ wed->wlan.wpdma_txfree = res->start + MT_RXQ_WED_RING_BASE;
+ wed->wlan.wpdma_rx_glo = res->start + MT_WPDMA_GLO_CFG;
+ wed->wlan.wpdma_rx = res->start + MT_RXQ_WED_DATA_RING_BASE;
+ }
+ wed->wlan.nbuf = 4096;
+ wed->wlan.tx_tbit[0] = is_mt7915(&dev->mt76) ? 4 : 30;
+ wed->wlan.tx_tbit[1] = is_mt7915(&dev->mt76) ? 5 : 31;
+ wed->wlan.txfree_tbit = is_mt7986(&dev->mt76) ? 2 : 1;
+ wed->wlan.token_start = MT7915_TOKEN_SIZE - wed->wlan.nbuf;
+ wed->wlan.wcid_512 = !is_mt7915(&dev->mt76);
+
+ wed->wlan.rx_nbuf = 65536;
+ wed->wlan.rx_npkt = MT7915_WED_RX_TOKEN_SIZE;
+ wed->wlan.rx_size = SKB_WITH_OVERHEAD(MT_RX_BUF_SIZE);
+ if (is_mt7915(&dev->mt76)) {
+ wed->wlan.rx_tbit[0] = 16;
+ wed->wlan.rx_tbit[1] = 17;
+ } else if (is_mt7986(&dev->mt76)) {
+ wed->wlan.rx_tbit[0] = 22;
+ wed->wlan.rx_tbit[1] = 23;
+ } else {
+ wed->wlan.rx_tbit[0] = 18;
+ wed->wlan.rx_tbit[1] = 19;
+ }
+
+ wed->wlan.init_buf = mt7915_wed_init_buf;
+ wed->wlan.offload_enable = mt7915_mmio_wed_offload_enable;
+ wed->wlan.offload_disable = mt7915_mmio_wed_offload_disable;
+ wed->wlan.init_rx_buf = mt7915_mmio_wed_init_rx_buf;
+ wed->wlan.release_rx_buf = mt7915_mmio_wed_release_rx_buf;
+ wed->wlan.update_wo_rx_stats = mt7915_mmio_wed_update_rx_stats;
+
+ dev->mt76.rx_token_size = wed->wlan.rx_npkt;
+
+ if (mtk_wed_device_attach(wed))
+ return 0;
+
+ *irq = wed->irq;
+ dev->mt76.dma_dev = wed->dev;
+
+ ret = dma_set_mask(wed->dev, DMA_BIT_MASK(32));
+ if (ret)
+ return ret;
+
+ return 1;
+#else
+ return 0;
+#endif
+}
+
static int mt7915_mmio_init(struct mt76_dev *mdev,
void __iomem *mem_base,
u32 device_id)
@@ -536,7 +854,11 @@ void mt7915_dual_hif_set_irq_mask(struct mt7915_dev *dev,
mdev->mmio.irqmask |= set;
if (write_reg) {
- mt76_wr(dev, MT_INT_MASK_CSR, mdev->mmio.irqmask);
+ if (mtk_wed_device_active(&mdev->mmio.wed))
+ mtk_wed_device_irq_set_mask(&mdev->mmio.wed,
+ mdev->mmio.irqmask);
+ else
+ mt76_wr(dev, MT_INT_MASK_CSR, mdev->mmio.irqmask);
mt76_wr(dev, MT_INT1_MASK_CSR, mdev->mmio.irqmask);
}
@@ -560,6 +882,8 @@ static void mt7915_irq_tasklet(struct tasklet_struct *t)
if (mtk_wed_device_active(wed)) {
mtk_wed_device_irq_set_mask(wed, 0);
+ if (dev->hif2)
+ mt76_wr(dev, MT_INT1_MASK_CSR, 0);
intr = mtk_wed_device_irq_get(wed, dev->mt76.mmio.irqmask);
} else {
mt76_wr(dev, MT_INT_MASK_CSR, 0);
@@ -613,10 +937,9 @@ static void mt7915_irq_tasklet(struct tasklet_struct *t)
u32 val = mt76_rr(dev, MT_MCU_CMD);
mt76_wr(dev, MT_MCU_CMD, val);
- if (val & MT_MCU_CMD_ERROR_MASK) {
- dev->reset_state = val;
- queue_work(dev->mt76.wq, &dev->reset_work);
- wake_up(&dev->reset_wait);
+ if (val & (MT_MCU_CMD_ERROR_MASK | MT_MCU_CMD_WDT_MASK)) {
+ dev->recovery.state = val;
+ mt7915_reset(dev);
}
}
}
@@ -648,7 +971,8 @@ struct mt7915_dev *mt7915_mmio_probe(struct device *pdev,
static const struct mt76_driver_ops drv_ops = {
/* txwi_size = txd size + txp size */
.txwi_size = MT_TXD_SIZE + sizeof(struct mt76_connac_fw_txp),
- .drv_flags = MT_DRV_TXWI_NO_FREE | MT_DRV_HW_MGMT_TXQ,
+ .drv_flags = MT_DRV_TXWI_NO_FREE | MT_DRV_HW_MGMT_TXQ |
+ MT_DRV_AMSDU_OFFLOAD,
.survey_flags = SURVEY_INFO_TIME_TX |
SURVEY_INFO_TIME_RX |
SURVEY_INFO_TIME_BSS_RX,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h
index 1eb11617a625..6351feba6bdf 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h
@@ -68,6 +68,8 @@
#define MT7915_MIN_TWT_DUR 64
#define MT7915_MAX_QUEUE (MT_RXQ_BAND2 + __MT_MCUQ_MAX + 2)
+#define MT7915_WED_RX_TOKEN_SIZE 12288
+
struct mt7915_vif;
struct mt7915_sta;
struct mt7915_dfs_pulse;
@@ -114,6 +116,8 @@ struct mt7915_twt_flow {
u8 sched:1;
};
+DECLARE_EWMA(avg_signal, 10, 8)
+
struct mt7915_sta {
struct mt76_wcid wcid; /* must be first */
@@ -123,10 +127,12 @@ struct mt7915_sta {
struct list_head rc_list;
u32 airtime_ac[8];
+ int ack_signal;
+ struct ewma_avg_signal avg_ack_signal;
+
unsigned long changed;
unsigned long jiffies;
unsigned long ampdu_state;
-
struct mt76_connac_sta_key_conf bip;
struct {
@@ -220,6 +226,15 @@ struct mib_stats {
u32 tx_amsdu_cnt;
};
+/* crash-dump */
+struct mt7915_crash_data {
+ guid_t guid;
+ struct timespec64 timestamp;
+
+ u8 *memdump_buf;
+ size_t memdump_buf_len;
+};
+
struct mt7915_hif {
struct list_head list;
@@ -243,7 +258,6 @@ struct mt7915_phy {
u32 rxfilter;
u64 omac_mask;
- u8 band_idx;
u16 noise;
@@ -301,9 +315,26 @@ struct mt7915_dev {
struct work_struct init_work;
struct work_struct rc_work;
+ struct work_struct dump_work;
struct work_struct reset_work;
wait_queue_head_t reset_wait;
- u32 reset_state;
+
+ struct {
+ u32 state;
+ u32 wa_reset_count;
+ u32 wm_reset_count;
+ bool hw_full_reset:1;
+ bool hw_init_done:1;
+ bool restart:1;
+ } recovery;
+
+ /* protects coredump data */
+ struct mutex dump_mutex;
+#ifdef CONFIG_DEV_COREDUMP
+ struct {
+ struct mt7915_crash_data *crash_data;
+ } coredump;
+#endif
struct list_head sta_rc_list;
struct list_head sta_poll_list;
@@ -357,6 +388,7 @@ enum mt7915_rdd_cmd {
RDD_DET_MODE,
RDD_RADAR_EMULATE,
RDD_START_TXQ = 20,
+ RDD_SET_WF_ANT = 30,
RDD_CAC_START = 50,
RDD_CAC_END,
RDD_NORMAL_START,
@@ -442,7 +474,14 @@ s8 mt7915_eeprom_get_power_delta(struct mt7915_dev *dev, int band);
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);
+int mt7915_dma_reset(struct mt7915_dev *dev, bool force);
+int mt7915_txbf_init(struct mt7915_dev *dev);
+void mt7915_init_txpower(struct mt7915_dev *dev,
+ struct ieee80211_supported_band *sband);
+void mt7915_reset(struct mt7915_dev *dev);
+int mt7915_run(struct ieee80211_hw *hw);
int mt7915_mcu_init(struct mt7915_dev *dev);
+int mt7915_mcu_init_firmware(struct mt7915_dev *dev);
int mt7915_mcu_twt_agrt_update(struct mt7915_dev *dev,
struct mt7915_vif *mvif,
struct mt7915_twt_flow *flow,
@@ -463,8 +502,8 @@ int mt7915_mcu_update_bss_color(struct mt7915_dev *dev, struct ieee80211_vif *vi
struct cfg80211_he_bss_color *he_bss_color);
int mt7915_mcu_add_beacon(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
int enable, u32 changed);
-int mt7915_mcu_add_obss_spr(struct mt7915_dev *dev, struct ieee80211_vif *vif,
- bool enable);
+int mt7915_mcu_add_obss_spr(struct mt7915_phy *phy, struct ieee80211_vif *vif,
+ struct ieee80211_he_obss_pd *he_obss_pd);
int mt7915_mcu_add_rate_ctrl(struct mt7915_dev *dev, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, bool changed);
int mt7915_mcu_add_smps(struct mt7915_dev *dev, struct ieee80211_vif *vif,
@@ -488,6 +527,10 @@ int mt7915_mcu_set_ser(struct mt7915_dev *dev, u8 action, u8 set, u8 band);
int mt7915_mcu_set_sku_en(struct mt7915_phy *phy, bool enable);
int mt7915_mcu_set_txpower_sku(struct mt7915_phy *phy);
int mt7915_mcu_get_txpower_sku(struct mt7915_phy *phy, s8 *txpower, int len);
+int mt7915_mcu_set_txpower_frame_min(struct mt7915_phy *phy, s8 txpower);
+int mt7915_mcu_set_txpower_frame(struct mt7915_phy *phy,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, s8 txpower);
int mt7915_mcu_set_txbf(struct mt7915_dev *dev, u8 action);
int mt7915_mcu_set_fcc5_lpn(struct mt7915_dev *dev, int val);
int mt7915_mcu_set_pulse_th(struct mt7915_dev *dev,
@@ -542,11 +585,17 @@ static inline void mt7915_irq_disable(struct mt7915_dev *dev, u32 mask)
mt76_set_irq_mask(&dev->mt76, MT_INT_MASK_CSR, mask, 0);
}
+void mt7915_memcpy_fromio(struct mt7915_dev *dev, void *buf, u32 offset,
+ size_t len);
+
+void mt7915_mac_init(struct mt7915_dev *dev);
u32 mt7915_mac_wtbl_lmac_addr(struct mt7915_dev *dev, u16 wcid, u8 dw);
bool mt7915_mac_wtbl_update(struct mt7915_dev *dev, int idx, u32 mask);
void mt7915_mac_reset_counters(struct mt7915_phy *phy);
void mt7915_mac_cca_stats_reset(struct mt7915_phy *phy);
void mt7915_mac_enable_nf(struct mt7915_dev *dev, bool ext_phy);
+void mt7915_mac_enable_rtscts(struct mt7915_dev *dev,
+ struct ieee80211_vif *vif, bool enable);
void mt7915_mac_write_txwi(struct mt76_dev *dev, __le32 *txwi,
struct sk_buff *skb, struct mt76_wcid *wcid, int pid,
struct ieee80211_key_conf *key,
@@ -558,6 +607,7 @@ void mt7915_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
struct ieee80211_sta *sta);
void mt7915_mac_work(struct work_struct *work);
void mt7915_mac_reset_work(struct work_struct *work);
+void mt7915_mac_dump_work(struct work_struct *work);
void mt7915_mac_sta_rc_work(struct work_struct *work);
void mt7915_mac_update_stats(struct mt7915_phy *phy);
void mt7915_mac_twt_teardown_flow(struct mt7915_dev *dev,
@@ -572,7 +622,7 @@ int mt7915_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
struct mt76_tx_info *tx_info);
void mt7915_tx_token_put(struct mt7915_dev *dev);
void mt7915_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
- struct sk_buff *skb);
+ struct sk_buff *skb, u32 *info);
bool mt7915_rx_check(struct mt76_dev *mdev, void *data, int len);
void mt7915_sta_ps(struct mt76_dev *mdev, struct ieee80211_sta *sta, bool ps);
void mt7915_stats_work(struct work_struct *work);
@@ -583,6 +633,7 @@ void mt7915_set_stream_vht_txbf_caps(struct mt7915_phy *phy);
void mt7915_update_channel(struct mt76_phy *mphy);
int mt7915_mcu_muru_debug_set(struct mt7915_dev *dev, bool enable);
int mt7915_mcu_muru_debug_get(struct mt7915_phy *phy, void *ms);
+int mt7915_mcu_wed_enable_rx_stats(struct mt7915_dev *dev);
int mt7915_init_debugfs(struct mt7915_phy *phy);
void mt7915_debugfs_rx_fw_monitor(struct mt7915_dev *dev, const void *data, int len);
bool mt7915_debugfs_rx_log(struct mt7915_dev *dev, const void *data, int len);
@@ -590,5 +641,7 @@ bool mt7915_debugfs_rx_log(struct mt7915_dev *dev, const void *data, int len);
void mt7915_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, struct dentry *dir);
#endif
+int mt7915_mmio_wed_init(struct mt7915_dev *dev, void *pdev_ptr,
+ bool pci, int *irq);
#endif
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/pci.c b/drivers/net/wireless/mediatek/mt76/mt7915/pci.c
index 728a879c3b00..39132894e8ea 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/pci.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/pci.c
@@ -12,9 +12,6 @@
#include "mac.h"
#include "../trace.h"
-static bool wed_enable = false;
-module_param(wed_enable, bool, 0644);
-
static LIST_HEAD(hif_list);
static DEFINE_SPINLOCK(hif_lock);
static u32 hif_idx;
@@ -65,10 +62,17 @@ static void mt7915_put_hif2(struct mt7915_hif *hif)
static struct mt7915_hif *mt7915_pci_init_hif2(struct pci_dev *pdev)
{
+ struct pci_dev *tmp_pdev;
+
hif_idx++;
- if (!pci_get_device(PCI_VENDOR_ID_MEDIATEK, 0x7916, NULL) &&
- !pci_get_device(PCI_VENDOR_ID_MEDIATEK, 0x790a, NULL))
- return NULL;
+
+ tmp_pdev = pci_get_device(PCI_VENDOR_ID_MEDIATEK, 0x7916, NULL);
+ if (!tmp_pdev) {
+ tmp_pdev = pci_get_device(PCI_VENDOR_ID_MEDIATEK, 0x790a, NULL);
+ if (!tmp_pdev)
+ return NULL;
+ }
+ pci_dev_put(tmp_pdev);
writel(hif_idx | MT_PCIE_RECOG_ID_SEM,
pcim_iomap_table(pdev)[0] + MT_PCIE_RECOG_ID);
@@ -95,94 +99,6 @@ static int mt7915_pci_hif2_probe(struct pci_dev *pdev)
return 0;
}
-#ifdef CONFIG_NET_MEDIATEK_SOC_WED
-static int mt7915_wed_offload_enable(struct mtk_wed_device *wed)
-{
- struct mt7915_dev *dev;
- struct mt7915_phy *phy;
- int ret;
-
- dev = container_of(wed, struct mt7915_dev, mt76.mmio.wed);
-
- spin_lock_bh(&dev->mt76.token_lock);
- dev->mt76.token_size = wed->wlan.token_start;
- spin_unlock_bh(&dev->mt76.token_lock);
-
- ret = wait_event_timeout(dev->mt76.tx_wait,
- !dev->mt76.wed_token_count, HZ);
- if (!ret)
- return -EAGAIN;
-
- phy = &dev->phy;
- mt76_set(dev, MT_AGG_ACR4(phy->band_idx), MT_AGG_ACR_PPDU_TXS2H);
-
- phy = dev->mt76.phys[MT_BAND1] ? dev->mt76.phys[MT_BAND1]->priv : NULL;
- if (phy)
- mt76_set(dev, MT_AGG_ACR4(phy->band_idx),
- MT_AGG_ACR_PPDU_TXS2H);
-
- return 0;
-}
-
-static void mt7915_wed_offload_disable(struct mtk_wed_device *wed)
-{
- struct mt7915_dev *dev;
- struct mt7915_phy *phy;
-
- dev = container_of(wed, struct mt7915_dev, mt76.mmio.wed);
-
- spin_lock_bh(&dev->mt76.token_lock);
- dev->mt76.token_size = MT7915_TOKEN_SIZE;
- spin_unlock_bh(&dev->mt76.token_lock);
-
- /* MT_TXD5_TX_STATUS_HOST (MPDU format) has higher priority than
- * MT_AGG_ACR_PPDU_TXS2H (PPDU format) even though ACR bit is set.
- */
- phy = &dev->phy;
- mt76_clear(dev, MT_AGG_ACR4(phy->band_idx), MT_AGG_ACR_PPDU_TXS2H);
-
- phy = dev->mt76.phys[MT_BAND1] ? dev->mt76.phys[MT_BAND1]->priv : NULL;
- if (phy)
- mt76_clear(dev, MT_AGG_ACR4(phy->band_idx),
- MT_AGG_ACR_PPDU_TXS2H);
-}
-#endif
-
-static int
-mt7915_pci_wed_init(struct mt7915_dev *dev, struct pci_dev *pdev, int *irq)
-{
-#ifdef CONFIG_NET_MEDIATEK_SOC_WED
- struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
- int ret;
-
- if (!wed_enable)
- return 0;
-
- wed->wlan.pci_dev = pdev;
- wed->wlan.wpdma_phys = pci_resource_start(pdev, 0) +
- MT_WFDMA_EXT_CSR_BASE;
- wed->wlan.nbuf = 4096;
- wed->wlan.token_start = MT7915_TOKEN_SIZE - wed->wlan.nbuf;
- wed->wlan.init_buf = mt7915_wed_init_buf;
- wed->wlan.offload_enable = mt7915_wed_offload_enable;
- wed->wlan.offload_disable = mt7915_wed_offload_disable;
-
- if (mtk_wed_device_attach(wed) != 0)
- return 0;
-
- *irq = wed->irq;
- dev->mt76.dma_dev = wed->dev;
-
- ret = dma_set_mask(wed->dev, DMA_BIT_MASK(32));
- if (ret)
- return ret;
-
- return 1;
-#else
- return 0;
-#endif
-}
-
static int mt7915_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
@@ -220,7 +136,7 @@ static int mt7915_pci_probe(struct pci_dev *pdev,
mt7915_wfsys_reset(dev);
hif2 = mt7915_pci_init_hif2(pdev);
- ret = mt7915_pci_wed_init(dev, pdev, &irq);
+ ret = mt7915_mmio_wed_init(dev, pdev, true, &irq);
if (ret < 0)
goto free_wed_or_irq_vector;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h
index 5920e705835a..aca1b2f1e9e3 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h
@@ -24,8 +24,26 @@ enum reg_rev {
WFDMA_EXT_CSR_ADDR,
CBTOP1_PHY_END,
INFRA_MCU_ADDR_END,
- FW_EXCEPTION_ADDR,
+ FW_ASSERT_STAT_ADDR,
+ FW_EXCEPT_TYPE_ADDR,
+ FW_EXCEPT_COUNT_ADDR,
+ FW_CIRQ_COUNT_ADDR,
+ FW_CIRQ_IDX_ADDR,
+ FW_CIRQ_LISR_ADDR,
+ FW_TASK_ID_ADDR,
+ FW_TASK_IDX_ADDR,
+ FW_TASK_QID1_ADDR,
+ FW_TASK_QID2_ADDR,
+ FW_TASK_START_ADDR,
+ FW_TASK_END_ADDR,
+ FW_TASK_SIZE_ADDR,
+ FW_LAST_MSG_ID_ADDR,
+ FW_EINT_INFO_ADDR,
+ FW_SCHED_INFO_ADDR,
SWDEF_BASE_ADDR,
+ TXQ_WED_RING_BASE,
+ RXQ_WED_RING_BASE,
+ RXQ_WED_DATA_RING_BASE,
__MT_REG_MAX,
};
@@ -224,6 +242,14 @@ enum offs_rev {
#define MT_DMA_DCR0_MAX_RX_LEN GENMASK(15, 3)
#define MT_DMA_DCR0_RXD_G5_EN BIT(23)
+/* WTBLOFF TOP: band 0(0x820e9000),band 1(0x820f9000) */
+#define MT_WTBLOFF_TOP_BASE(_band) ((_band) ? 0x820f9000 : 0x820e9000)
+#define MT_WTBLOFF_TOP(_band, ofs) (MT_WTBLOFF_TOP_BASE(_band) + (ofs))
+
+#define MT_WTBLOFF_TOP_RSCR(_band) MT_WTBLOFF_TOP(_band, 0x008)
+#define MT_WTBLOFF_TOP_RSCR_RCPI_MODE GENMASK(31, 30)
+#define MT_WTBLOFF_TOP_RSCR_RCPI_PARAM GENMASK(25, 24)
+
/* ETBF: band 0(0x820ea000), band 1(0x820fa000) */
#define MT_WF_ETBF_BASE(_band) ((_band) ? 0x820fa000 : 0x820ea000)
#define MT_WF_ETBF(_band, ofs) (MT_WF_ETBF_BASE(_band) + (ofs))
@@ -523,8 +549,22 @@ enum offs_rev {
#define MT_WF_RFCR1_DROP_CFEND BIT(7)
#define MT_WF_RFCR1_DROP_CFACK BIT(8)
+#define MT_WF_RMAC_RSVD0(_band) MT_WF_RMAC(_band, 0x02e0)
+#define MT_WF_RMAC_RSVD0_EIFS_CLR BIT(21)
+
#define MT_WF_RMAC_MIB_AIRTIME0(_band) MT_WF_RMAC(_band, 0x0380)
#define MT_WF_RMAC_MIB_RXTIME_CLR BIT(31)
+#define MT_WF_RMAC_MIB_OBSS_BACKOFF GENMASK(15, 0)
+#define MT_WF_RMAC_MIB_ED_OFFSET GENMASK(20, 16)
+
+#define MT_WF_RMAC_MIB_AIRTIME1(_band) MT_WF_RMAC(_band, 0x0384)
+#define MT_WF_RMAC_MIB_NONQOSD_BACKOFF GENMASK(31, 16)
+
+#define MT_WF_RMAC_MIB_AIRTIME3(_band) MT_WF_RMAC(_band, 0x038c)
+#define MT_WF_RMAC_MIB_QOS01_BACKOFF GENMASK(31, 0)
+
+#define MT_WF_RMAC_MIB_AIRTIME4(_band) MT_WF_RMAC(_band, 0x0390)
+#define MT_WF_RMAC_MIB_QOS23_BACKOFF GENMASK(31, 0)
/* WFDMA0 */
#define MT_WFDMA0_BASE __REG(WFDMA0_ADDR)
@@ -539,6 +579,8 @@ enum offs_rev {
#define MT_WFDMA0_BUSY_ENA_TX_FIFO1 BIT(1)
#define MT_WFDMA0_BUSY_ENA_RX_FIFO BIT(2)
+#define MT_WFDMA0_MCU_HOST_INT_ENA MT_WFDMA0(0x1f4)
+
#define MT_WFDMA0_GLO_CFG MT_WFDMA0(0x208)
#define MT_WFDMA0_GLO_CFG_TX_DMA_EN BIT(0)
#define MT_WFDMA0_GLO_CFG_RX_DMA_EN BIT(2)
@@ -547,9 +589,14 @@ enum offs_rev {
#define MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2 BIT(21)
#define MT_WFDMA0_RST_DTX_PTR MT_WFDMA0(0x20c)
+
+#define MT_WFDMA0_EXT0_CFG MT_WFDMA0(0x2b0)
+#define MT_WFDMA0_EXT0_RXWB_KEEP BIT(10)
+
#define MT_WFDMA0_PRI_DLY_INT_CFG0 MT_WFDMA0(0x2f0)
#define MT_WFDMA0_PRI_DLY_INT_CFG1 MT_WFDMA0(0x2f4)
#define MT_WFDMA0_PRI_DLY_INT_CFG2 MT_WFDMA0(0x2f8)
+#define MT_WPDMA_GLO_CFG MT_WFDMA0(0x208)
/* WFDMA1 */
#define MT_WFDMA1_BASE 0xd5000
@@ -596,6 +643,7 @@ enum offs_rev {
#define MT_PCIE_RECOG_ID_MASK GENMASK(30, 0)
#define MT_PCIE_RECOG_ID_SEM BIT(31)
+#define MT_INT_WED_SOURCE_CSR MT_WFDMA_EXT_CSR(0x200)
#define MT_INT_WED_MASK_CSR MT_WFDMA_EXT_CSR(0x204)
#define MT_WED_TX_RING_BASE MT_WFDMA_EXT_CSR(0x300)
@@ -642,6 +690,10 @@ enum offs_rev {
#define MT_TXQ_EXT_CTRL(q) (MT_Q_BASE(__TXQ(q)) + 0x600 + \
MT_TXQ_ID(q)* 0x4)
+#define MT_TXQ_WED_RING_BASE __REG(TXQ_WED_RING_BASE)
+#define MT_RXQ_WED_RING_BASE __REG(RXQ_WED_RING_BASE)
+#define MT_RXQ_WED_DATA_RING_BASE __REG(RXQ_WED_DATA_RING_BASE)
+
#define MT_INT_SOURCE_CSR __REG(INT_SOURCE_CSR)
#define MT_INT_MASK_CSR __REG(INT_MASK_CSR)
@@ -660,6 +712,11 @@ enum offs_rev {
#define MT_INT_RX_DONE_WA_MAIN_MT7916 BIT(2)
#define MT_INT_RX_DONE_WA_EXT_MT7916 BIT(3)
+#define MT_INT_WED_RX_DONE_BAND0_MT7916 BIT(18)
+#define MT_INT_WED_RX_DONE_BAND1_MT7916 BIT(19)
+#define MT_INT_WED_RX_DONE_WA_MAIN_MT7916 BIT(1)
+#define MT_INT_WED_RX_DONE_WA_MT7916 BIT(17)
+
#define MT_INT_RX(q) (dev->q_int_mask[__RXQ(q)])
#define MT_INT_TX_MCU(q) (dev->q_int_mask[(q)])
@@ -683,6 +740,8 @@ enum offs_rev {
#define MT_INT_TX_DONE_BAND0 BIT(30)
#define MT_INT_TX_DONE_BAND1 BIT(31)
#define MT_INT_TX_DONE_MCU_WA_MT7916 BIT(25)
+#define MT_INT_WED_TX_DONE_BAND0 BIT(4)
+#define MT_INT_WED_TX_DONE_BAND1 BIT(5)
#define MT_INT_TX_DONE_MCU (MT_INT_TX_MCU(MT_MCUQ_WA) | \
MT_INT_TX_MCU(MT_MCUQ_WM) | \
@@ -696,6 +755,10 @@ enum offs_rev {
#define MT_MCU_CMD_NORMAL_STATE BIT(5)
#define MT_MCU_CMD_ERROR_MASK GENMASK(5, 1)
+#define MT_MCU_CMD_WA_WDT BIT(31)
+#define MT_MCU_CMD_WM_WDT BIT(30)
+#define MT_MCU_CMD_WDT_MASK GENMASK(31, 30)
+
/* TOP RGU */
#define MT_TOP_RGU_BASE 0x18000000
#define MT_TOP_PWR_CTRL (MT_TOP_RGU_BASE + (0x0))
@@ -938,7 +1001,22 @@ enum offs_rev {
#define MT_ADIE_TYPE_MASK BIT(1)
/* FW MODE SYNC */
-#define MT_FW_EXCEPTION __REG(FW_EXCEPTION_ADDR)
+#define MT_FW_ASSERT_STAT __REG(FW_ASSERT_STAT_ADDR)
+#define MT_FW_EXCEPT_TYPE __REG(FW_EXCEPT_TYPE_ADDR)
+#define MT_FW_EXCEPT_COUNT __REG(FW_EXCEPT_COUNT_ADDR)
+#define MT_FW_CIRQ_COUNT __REG(FW_CIRQ_COUNT_ADDR)
+#define MT_FW_CIRQ_IDX __REG(FW_CIRQ_IDX_ADDR)
+#define MT_FW_CIRQ_LISR __REG(FW_CIRQ_LISR_ADDR)
+#define MT_FW_TASK_ID __REG(FW_TASK_ID_ADDR)
+#define MT_FW_TASK_IDX __REG(FW_TASK_IDX_ADDR)
+#define MT_FW_TASK_QID1 __REG(FW_TASK_QID1_ADDR)
+#define MT_FW_TASK_QID2 __REG(FW_TASK_QID2_ADDR)
+#define MT_FW_TASK_START __REG(FW_TASK_START_ADDR)
+#define MT_FW_TASK_END __REG(FW_TASK_END_ADDR)
+#define MT_FW_TASK_SIZE __REG(FW_TASK_SIZE_ADDR)
+#define MT_FW_LAST_MSG_ID __REG(FW_LAST_MSG_ID_ADDR)
+#define MT_FW_EINT_INFO __REG(FW_EINT_INFO_ADDR)
+#define MT_FW_SCHED_INFO __REG(FW_SCHED_INFO_ADDR)
#define MT_SWDEF_BASE __REG(SWDEF_BASE_ADDR)
@@ -1108,9 +1186,15 @@ enum offs_rev {
#define MT_WF_PHY_RXTD12_IRPI_SW_CLR_ONLY BIT(18)
#define MT_WF_PHY_RXTD12_IRPI_SW_CLR BIT(29)
+#define MT_WF_PHY_TPC_CTRL_STAT(_phy) MT_WF_PHY(0xe7a0 + ((_phy) << 16))
+#define MT_WF_PHY_TPC_CTRL_STAT_MT7916(_phy) MT_WF_PHY(0xe7a0 + ((_phy) << 20))
+#define MT_WF_PHY_TPC_POWER GENMASK(15, 8)
+
#define MT_MCU_WM_CIRQ_BASE 0x89010000
#define MT_MCU_WM_CIRQ(ofs) (MT_MCU_WM_CIRQ_BASE + (ofs))
#define MT_MCU_WM_CIRQ_IRQ_MASK_CLR_ADDR MT_MCU_WM_CIRQ(0x80)
#define MT_MCU_WM_CIRQ_IRQ_SOFT_ADDR MT_MCU_WM_CIRQ(0xc0)
+#define MT_MCU_WM_CIRQ_EINT_MASK_CLR_ADDR MT_MCU_WM_CIRQ(0x108)
+#define MT_MCU_WM_CIRQ_EINT_SOFT_ADDR MT_MCU_WM_CIRQ(0x118)
#endif
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/soc.c b/drivers/net/wireless/mediatek/mt76/mt7915/soc.c
index c74afa746251..c06c56a0270d 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/soc.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/soc.c
@@ -1172,10 +1172,6 @@ static int mt7986_wmac_probe(struct platform_device *pdev)
chip_id = (uintptr_t)of_device_get_match_data(&pdev->dev);
- irq = platform_get_irq(pdev, 0);
- if (irq < 0)
- return irq;
-
mem_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(mem_base)) {
dev_err(&pdev->dev, "Failed to get memory resource\n");
@@ -1187,6 +1183,18 @@ static int mt7986_wmac_probe(struct platform_device *pdev)
return PTR_ERR(dev);
mdev = &dev->mt76;
+ ret = mt7915_mmio_wed_init(dev, pdev, false, &irq);
+ if (ret < 0)
+ goto free_device;
+
+ if (!ret) {
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ ret = irq;
+ goto free_device;
+ }
+ }
+
ret = devm_request_irq(mdev->dev, irq, mt7915_irq_handler,
IRQF_SHARED, KBUILD_MODNAME, dev);
if (ret)
@@ -1206,9 +1214,10 @@ static int mt7986_wmac_probe(struct platform_device *pdev)
free_irq:
devm_free_irq(mdev->dev, irq, dev);
-
free_device:
- mt76_free_device(&dev->mt76);
+ if (mtk_wed_device_active(&mdev->mmio.wed))
+ mtk_wed_device_detach(&mdev->mmio.wed);
+ mt76_free_device(mdev);
return ret;
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c b/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c
index efb9bb8231e2..0d76ae31b376 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c
@@ -44,14 +44,14 @@ mt7915_tm_set_tx_power(struct mt7915_phy *phy)
int ret;
struct {
u8 format_id;
- u8 dbdc_idx;
+ u8 band_idx;
s8 tx_power;
u8 ant_idx; /* Only 0 is valid */
u8 center_chan;
u8 rsv[3];
} __packed req = {
.format_id = 0xf,
- .dbdc_idx = phy != &dev->phy,
+ .band_idx = phy->mt76->band_idx,
.center_chan = ieee80211_frequency_to_channel(freq),
};
u8 *tx_power = NULL;
@@ -77,7 +77,7 @@ mt7915_tm_set_freq_offset(struct mt7915_phy *phy, bool en, u32 val)
struct mt7915_tm_cmd req = {
.testmode_en = en,
.param_idx = MCU_ATE_SET_FREQ_OFFSET,
- .param.freq.band = phy != &dev->phy,
+ .param.freq.band = phy->mt76->band_idx,
.param.freq.freq_offset = cpu_to_le32(val),
};
@@ -111,7 +111,7 @@ mt7915_tm_set_trx(struct mt7915_phy *phy, int type, bool en)
.param_idx = MCU_ATE_SET_TRX,
.param.trx.type = type,
.param.trx.enable = en,
- .param.trx.band = phy != &dev->phy,
+ .param.trx.band = phy->mt76->band_idx,
};
return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(ATE_CTRL), &req,
@@ -126,7 +126,7 @@ mt7915_tm_clean_hwq(struct mt7915_phy *phy, u8 wcid)
.testmode_en = 1,
.param_idx = MCU_ATE_CLEAN_TXQUEUE,
.param.clean.wcid = wcid,
- .param.clean.band = phy != &dev->phy,
+ .param.clean.band = phy->mt76->band_idx,
};
return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(ATE_CTRL), &req,
@@ -144,7 +144,7 @@ mt7915_tm_set_slot_time(struct mt7915_phy *phy, u8 slot_time, u8 sifs)
.param.slot.sifs = sifs,
.param.slot.rifs = 2,
.param.slot.eifs = cpu_to_le16(60),
- .param.slot.band = phy != &dev->phy,
+ .param.slot.band = phy->mt76->band_idx,
};
return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(ATE_CTRL), &req,
@@ -198,6 +198,7 @@ mt7915_tm_set_ipg_params(struct mt7915_phy *phy, u32 ipg, u8 mode)
u8 sig_ext = (mode == MT76_TM_TX_MODE_CCK) ? 0 : 6;
u8 slot_time = 9, sifs = TM_DEFAULT_SIFS;
u8 aifsn = TM_MIN_AIFSN;
+ u8 band = phy->mt76->band_idx;
u32 i2t_time, tr2t_time, txv_time;
u16 cw = 0;
@@ -232,14 +233,14 @@ mt7915_tm_set_ipg_params(struct mt7915_phy *phy, u32 ipg, u8 mode)
sifs = min_t(u32, ipg, TM_MAX_SIFS);
}
done:
- txv_time = mt76_get_field(dev, MT_TMAC_ATCR(phy->band_idx),
+ txv_time = mt76_get_field(dev, MT_TMAC_ATCR(band),
MT_TMAC_ATCR_TXV_TOUT);
txv_time *= 50; /* normal clock time */
i2t_time = (slot_time * 1000 - txv_time - BBP_PROC_TIME) / 50;
tr2t_time = (sifs * 1000 - txv_time - BBP_PROC_TIME) / 50;
- mt76_set(dev, MT_TMAC_TRCR0(phy->band_idx),
+ mt76_set(dev, MT_TMAC_TRCR0(band),
FIELD_PREP(MT_TMAC_TRCR0_TR2T_CHK, tr2t_time) |
FIELD_PREP(MT_TMAC_TRCR0_I2T_CHK, i2t_time));
@@ -336,6 +337,7 @@ mt7915_tm_reg_backup_restore(struct mt7915_phy *phy)
int n_regs = ARRAY_SIZE(reg_backup_list);
struct mt7915_dev *dev = phy->dev;
u32 *b = phy->test.reg_backup;
+ u8 band = phy->mt76->band_idx;
int i;
REG_BAND_IDX(reg_backup_list[0], AGG_PCR0, 0);
@@ -358,7 +360,7 @@ mt7915_tm_reg_backup_restore(struct mt7915_phy *phy)
if (phy->mt76->test.state == MT76_TM_STATE_OFF) {
for (i = 0; i < n_regs; i++)
- mt76_wr(dev, reg_backup_list[i].band[phy->band_idx], b[i]);
+ mt76_wr(dev, reg_backup_list[i].band[band], b[i]);
return;
}
@@ -369,33 +371,33 @@ mt7915_tm_reg_backup_restore(struct mt7915_phy *phy)
phy->test.reg_backup = b;
for (i = 0; i < n_regs; i++)
- b[i] = mt76_rr(dev, reg_backup_list[i].band[phy->band_idx]);
+ b[i] = mt76_rr(dev, reg_backup_list[i].band[band]);
}
- mt76_clear(dev, MT_AGG_PCR0(phy->band_idx, 0), MT_AGG_PCR0_MM_PROT |
+ mt76_clear(dev, MT_AGG_PCR0(band, 0), MT_AGG_PCR0_MM_PROT |
MT_AGG_PCR0_GF_PROT | MT_AGG_PCR0_ERP_PROT |
MT_AGG_PCR0_VHT_PROT | MT_AGG_PCR0_BW20_PROT |
MT_AGG_PCR0_BW40_PROT | MT_AGG_PCR0_BW80_PROT);
- mt76_set(dev, MT_AGG_PCR0(phy->band_idx, 0), MT_AGG_PCR0_PTA_WIN_DIS);
+ mt76_set(dev, MT_AGG_PCR0(band, 0), MT_AGG_PCR0_PTA_WIN_DIS);
- mt76_wr(dev, MT_AGG_PCR0(phy->band_idx, 1), MT_AGG_PCR1_RTS0_NUM_THRES |
+ mt76_wr(dev, MT_AGG_PCR0(band, 1), MT_AGG_PCR1_RTS0_NUM_THRES |
MT_AGG_PCR1_RTS0_LEN_THRES);
- mt76_clear(dev, MT_AGG_MRCR(phy->band_idx), MT_AGG_MRCR_BAR_CNT_LIMIT |
+ mt76_clear(dev, MT_AGG_MRCR(band), MT_AGG_MRCR_BAR_CNT_LIMIT |
MT_AGG_MRCR_LAST_RTS_CTS_RN | MT_AGG_MRCR_RTS_FAIL_LIMIT |
MT_AGG_MRCR_TXCMD_RTS_FAIL_LIMIT);
- mt76_rmw(dev, MT_AGG_MRCR(phy->band_idx), MT_AGG_MRCR_RTS_FAIL_LIMIT |
+ mt76_rmw(dev, MT_AGG_MRCR(band), MT_AGG_MRCR_RTS_FAIL_LIMIT |
MT_AGG_MRCR_TXCMD_RTS_FAIL_LIMIT,
FIELD_PREP(MT_AGG_MRCR_RTS_FAIL_LIMIT, 1) |
FIELD_PREP(MT_AGG_MRCR_TXCMD_RTS_FAIL_LIMIT, 1));
- mt76_wr(dev, MT_TMAC_TFCR0(phy->band_idx), 0);
- mt76_clear(dev, MT_TMAC_TCR0(phy->band_idx), MT_TMAC_TCR0_TBTT_STOP_CTRL);
+ mt76_wr(dev, MT_TMAC_TFCR0(band), 0);
+ mt76_clear(dev, MT_TMAC_TCR0(band), MT_TMAC_TCR0_TBTT_STOP_CTRL);
/* config rx filter for testmode rx */
- mt76_wr(dev, MT_WF_RFCR(phy->band_idx), 0xcf70a);
- mt76_wr(dev, MT_WF_RFCR1(phy->band_idx), 0);
+ mt76_wr(dev, MT_WF_RFCR(band), 0xcf70a);
+ mt76_wr(dev, MT_WF_RFCR1(band), 0);
}
static void
@@ -432,8 +434,6 @@ mt7915_tm_update_channel(struct mt7915_phy *phy)
static void
mt7915_tm_set_tx_frames(struct mt7915_phy *phy, bool en)
{
- static const u8 spe_idx_map[] = {0, 0, 1, 0, 3, 2, 4, 0,
- 9, 8, 6, 10, 16, 12, 18, 0};
struct mt76_testmode_data *td = &phy->mt76->test;
struct mt7915_dev *dev = phy->dev;
struct ieee80211_tx_info *info;
@@ -447,15 +447,10 @@ mt7915_tm_set_tx_frames(struct mt7915_phy *phy, bool en)
if (en) {
mt7915_tm_update_channel(phy);
- if (td->tx_spe_idx) {
+ if (td->tx_spe_idx)
phy->test.spe_idx = td->tx_spe_idx;
- } else {
- u8 tx_ant = td->tx_antenna_mask;
-
- if (phy != &dev->phy)
- tx_ant >>= dev->chainshift;
- phy->test.spe_idx = spe_idx_map[tx_ant];
- }
+ else
+ phy->test.spe_idx = mt76_connac_spe_idx(td->tx_antenna_mask);
}
mt7915_tm_set_tam_arb(phy, en,
@@ -495,7 +490,7 @@ mt7915_tm_set_rx_frames(struct mt7915_phy *phy, bool en)
mt7915_tm_update_channel(phy);
/* read-clear */
- mt76_rr(dev, MT_MIB_SDR3(phy != &dev->phy));
+ mt76_rr(dev, MT_MIB_SDR3(phy->mt76->band_idx));
mt7915_tm_set_trx(phy, TM_MAC_RX_RXV, en);
}
}
@@ -522,6 +517,7 @@ mt7915_tm_set_tx_cont(struct mt7915_phy *phy, bool en)
struct mt76_testmode_data *td = &phy->mt76->test;
u32 func_idx = en ? TX_CONT_START : TX_CONT_STOP;
u8 rate_idx = td->tx_rate_idx, mode;
+ u8 band = phy->mt76->band_idx;
u16 rateval;
struct mt7915_tm_rf_test req = {
.action = 1,
@@ -533,7 +529,7 @@ mt7915_tm_set_tx_cont(struct mt7915_phy *phy, bool en)
tx_cont->control_ch = chandef->chan->hw_value;
tx_cont->center_ch = freq1;
tx_cont->tx_ant = td->tx_antenna_mask;
- tx_cont->band = phy != &dev->phy;
+ tx_cont->band = band;
switch (chandef->width) {
case NL80211_CHAN_WIDTH_40:
@@ -565,7 +561,7 @@ mt7915_tm_set_tx_cont(struct mt7915_phy *phy, bool en)
}
if (!en) {
- req.op.rf.param.func_data = cpu_to_le32(phy != &dev->phy);
+ req.op.rf.param.func_data = cpu_to_le32(band);
goto out;
}
@@ -696,7 +692,9 @@ mt7915_tm_set_params(struct mt76_phy *mphy, struct nlattr **tb,
{
struct mt76_testmode_data *td = &mphy->test;
struct mt7915_phy *phy = mphy->priv;
- u32 changed = 0;
+ struct mt7915_dev *dev = phy->dev;
+ u32 chainmask = mphy->chainmask, changed = 0;
+ bool ext_phy = phy != &dev->phy;
int i;
BUILD_BUG_ON(NUM_TM_CHANGED >= 32);
@@ -705,7 +703,8 @@ mt7915_tm_set_params(struct mt76_phy *mphy, struct nlattr **tb,
td->state == MT76_TM_STATE_OFF)
return 0;
- if (td->tx_antenna_mask & ~mphy->chainmask)
+ chainmask = ext_phy ? chainmask >> dev->chainshift : chainmask;
+ if (td->tx_antenna_mask > chainmask)
return -EINVAL;
for (i = 0; i < ARRAY_SIZE(tm_change_map); i++) {
@@ -771,11 +770,11 @@ mt7915_tm_dump_stats(struct mt76_phy *mphy, struct sk_buff *msg)
nla_nest_end(msg, rx);
- cnt = mt76_rr(dev, MT_MIB_SDR3(phy->band_idx));
+ cnt = mt76_rr(dev, MT_MIB_SDR3(phy->mt76->band_idx));
fcs_err = is_mt7915(&dev->mt76) ? FIELD_GET(MT_MIB_SDR3_FCS_ERR_MASK, cnt) :
FIELD_GET(MT_MIB_SDR3_FCS_ERR_MASK_MT7916, cnt);
- q = phy->band_idx ? MT_RXQ_BAND1 : MT_RXQ_MAIN;
+ q = phy->mt76->band_idx ? MT_RXQ_BAND1 : MT_RXQ_MAIN;
mphy->test.rx_stats.packets[q] += fcs_err;
mphy->test.rx_stats.fcs_error[q] += fcs_err;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7921/debugfs.c
index bce76417f95d..29d8883268f6 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/debugfs.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/debugfs.c
@@ -85,7 +85,7 @@ mt7921_ampdu_stat_read_phy(struct mt7921_phy *phy,
seq_puts(file, "\nCount: ");
for (i = 0; i < ARRAY_SIZE(bound); i++)
- seq_printf(file, "%8d | ", dev->mt76.aggr_stats[i]);
+ seq_printf(file, "%8d | ", phy->mt76->aggr_stats[i]);
seq_puts(file, "\n");
seq_printf(file, "BA miss count: %d\n", phy->mib.ba_miss_cnt);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/init.c b/drivers/net/wireless/mediatek/mt76/mt7921/init.c
index dcdb3cf04ac1..542dfd425129 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/init.c
@@ -2,6 +2,7 @@
/* Copyright (C) 2020 MediaTek Inc. */
#include <linux/etherdevice.h>
+#include <linux/firmware.h>
#include "mt7921.h"
#include "mac.h"
#include "mcu.h"
@@ -25,6 +26,27 @@ static const struct ieee80211_iface_combination if_comb[] = {
.max_interfaces = MT7921_MAX_INTERFACES,
.num_different_channels = 1,
.beacon_int_infra_match = true,
+ },
+};
+
+static const struct ieee80211_iface_limit if_limits_chanctx[] = {
+ {
+ .max = 2,
+ .types = BIT(NL80211_IFTYPE_STATION),
+ },
+ {
+ .max = 1,
+ .types = BIT(NL80211_IFTYPE_AP),
+ }
+};
+
+static const struct ieee80211_iface_combination if_comb_chanctx[] = {
+ {
+ .limits = if_limits_chanctx,
+ .n_limits = ARRAY_SIZE(if_limits_chanctx),
+ .max_interfaces = 2,
+ .num_different_channels = 2,
+ .beacon_int_infra_match = false,
}
};
@@ -37,6 +59,7 @@ mt7921_regd_notifier(struct wiphy *wiphy,
memcpy(dev->mt76.alpha2, request->alpha2, sizeof(dev->mt76.alpha2));
dev->mt76.region = request->dfs_region;
+ dev->country_ie_env = request->country_ie_env;
mt7921_mutex_acquire(dev);
mt7921_mcu_set_clc(dev, request->alpha2, request->country_ie_env);
@@ -65,12 +88,20 @@ mt7921_init_wiphy(struct ieee80211_hw *hw)
hw->sta_data_size = sizeof(struct mt7921_sta);
hw->vif_data_size = sizeof(struct mt7921_vif);
- wiphy->iface_combinations = if_comb;
+ if (dev->fw_features & MT7921_FW_CAP_CNM) {
+ wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
+ wiphy->iface_combinations = if_comb_chanctx;
+ wiphy->n_iface_combinations = ARRAY_SIZE(if_comb_chanctx);
+ } else {
+ wiphy->flags &= ~WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
+ wiphy->iface_combinations = if_comb;
+ wiphy->n_iface_combinations = ARRAY_SIZE(if_comb);
+ }
wiphy->flags &= ~(WIPHY_FLAG_IBSS_RSN | WIPHY_FLAG_4ADDR_AP |
WIPHY_FLAG_4ADDR_STATION);
wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_AP);
- wiphy->n_iface_combinations = ARRAY_SIZE(if_comb);
+ wiphy->max_remain_on_channel_duration = 5000;
wiphy->max_scan_ie_len = MT76_CONNAC_SCAN_IE_LEN;
wiphy->max_scan_ssids = 4;
wiphy->max_sched_scan_plan_interval =
@@ -129,6 +160,58 @@ mt7921_mac_init_band(struct mt7921_dev *dev, u8 band)
mt76_clear(dev, MT_DMA_DCR0(band), MT_DMA_DCR0_RXD_G5_EN);
}
+u8 mt7921_check_offload_capability(struct device *dev, const char *fw_wm)
+{
+ struct mt7921_fw_features *features = NULL;
+ const struct mt76_connac2_fw_trailer *hdr;
+ struct mt7921_realease_info *rel_info;
+ const struct firmware *fw;
+ int ret, i, offset = 0;
+ const u8 *data, *end;
+
+ ret = request_firmware(&fw, fw_wm, dev);
+ if (ret)
+ return ret;
+
+ if (!fw || !fw->data || fw->size < sizeof(*hdr)) {
+ dev_err(dev, "Invalid firmware\n");
+ return -EINVAL;
+ }
+
+ data = fw->data;
+ hdr = (const void *)(fw->data + fw->size - sizeof(*hdr));
+
+ for (i = 0; i < hdr->n_region; i++) {
+ const struct mt76_connac2_fw_region *region;
+
+ region = (const void *)((const u8 *)hdr -
+ (hdr->n_region - i) * sizeof(*region));
+ offset += le32_to_cpu(region->len);
+ }
+
+ data += offset + 16;
+ rel_info = (struct mt7921_realease_info *)data;
+ data += sizeof(*rel_info);
+ end = data + le16_to_cpu(rel_info->len);
+
+ while (data < end) {
+ rel_info = (struct mt7921_realease_info *)data;
+ data += sizeof(*rel_info);
+
+ if (rel_info->tag == MT7921_FW_TAG_FEATURE) {
+ features = (struct mt7921_fw_features *)data;
+ break;
+ }
+
+ data += le16_to_cpu(rel_info->len) + rel_info->pad_len;
+ }
+
+ release_firmware(fw);
+
+ return features ? features->data : 0;
+}
+EXPORT_SYMBOL_GPL(mt7921_check_offload_capability);
+
int mt7921_mac_init(struct mt7921_dev *dev)
{
int i;
@@ -278,6 +361,10 @@ int mt7921_register_device(struct mt7921_dev *dev)
INIT_WORK(&dev->reset_work, mt7921_mac_reset_work);
INIT_WORK(&dev->init_work, mt7921_init_work);
+ INIT_WORK(&dev->phy.roc_work, mt7921_roc_work);
+ timer_setup(&dev->phy.roc_timer, mt7921_roc_timer, 0);
+ init_waitqueue_head(&dev->phy.roc_wait);
+
dev->pm.idle_timeout = MT7921_PM_TIMEOUT;
dev->pm.stats.last_wake_event = jiffies;
dev->pm.stats.last_doze_event = jiffies;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c
index 650ab97ae052..82db3762be33 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c
@@ -168,14 +168,6 @@ static void
mt7921_get_status_freq_info(struct mt7921_dev *dev, struct mt76_phy *mphy,
struct mt76_rx_status *status, u8 chfreq)
{
- if (!test_bit(MT76_HW_SCANNING, &mphy->state) &&
- !test_bit(MT76_HW_SCHED_SCANNING, &mphy->state) &&
- !test_bit(MT76_STATE_ROC, &mphy->state)) {
- status->freq = mphy->chandef.chan->center_freq;
- status->band = mphy->chandef.chan->band;
- return;
- }
-
if (chfreq > 180) {
status->band = NL80211_BAND_6GHZ;
chfreq = (chfreq - 181) * 4 + 1;
@@ -396,6 +388,27 @@ mt7921_mac_fill_rx(struct mt7921_dev *dev, struct sk_buff *skb)
if (v0 & MT_PRXV_HT_AD_CODE)
status->enc_flags |= RX_ENC_FLAG_LDPC;
+ ret = mt76_connac2_mac_fill_rx_rate(&dev->mt76, status, sband,
+ rxv, &mode);
+ if (ret < 0)
+ return ret;
+
+ if (rxd1 & MT_RXD1_NORMAL_GROUP_5) {
+ rxd += 6;
+ if ((u8 *)rxd - skb->data >= skb->len)
+ return -EINVAL;
+
+ rxv = rxd;
+ /* Monitor mode would use RCPI described in GROUP 5
+ * instead.
+ */
+ v1 = le32_to_cpu(rxv[0]);
+
+ rxd += 12;
+ if ((u8 *)rxd - skb->data >= skb->len)
+ return -EINVAL;
+ }
+
status->chains = mphy->antenna_mask;
status->chain_signal[0] = to_rssi(MT_PRXV_RCPI0, v1);
status->chain_signal[1] = to_rssi(MT_PRXV_RCPI1, v1);
@@ -410,17 +423,6 @@ mt7921_mac_fill_rx(struct mt7921_dev *dev, struct sk_buff *skb)
status->signal = max(status->signal,
status->chain_signal[i]);
}
-
- ret = mt76_connac2_mac_fill_rx_rate(&dev->mt76, status, sband,
- rxv, &mode);
- if (ret < 0)
- return ret;
-
- if (rxd1 & MT_RXD1_NORMAL_GROUP_5) {
- rxd += 18;
- if ((u8 *)rxd - skb->data >= skb->len)
- return -EINVAL;
- }
}
amsdu_info = FIELD_GET(MT_RXD4_NORMAL_PAYLOAD_FORMAT, rxd4);
@@ -682,7 +684,7 @@ bool mt7921_rx_check(struct mt76_dev *mdev, void *data, int len)
EXPORT_SYMBOL_GPL(mt7921_rx_check);
void mt7921_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
- struct sk_buff *skb)
+ struct sk_buff *skb, u32 *info)
{
struct mt7921_dev *dev = container_of(mdev, struct mt7921_dev, mt76);
__le32 *rxd = (__le32 *)skb->data;
@@ -735,7 +737,7 @@ void mt7921_mac_reset_counters(struct mt7921_phy *phy)
}
dev->mt76.phy.survey_time = ktime_get_boottime();
- memset(&dev->mt76.aggr_stats[0], 0, sizeof(dev->mt76.aggr_stats) / 2);
+ memset(phy->mt76->aggr_stats, 0, sizeof(phy->mt76->aggr_stats));
/* reset airtime counters */
mt76_rr(dev, MT_MIB_SDR9(0));
@@ -856,7 +858,7 @@ mt7921_vif_connect_iter(void *priv, u8 *mac,
if (vif->type == NL80211_IFTYPE_AP) {
mt76_connac_mcu_uni_add_bss(dev->phy.mt76, vif, &mvif->sta.wcid,
- true);
+ true, NULL);
mt7921_mcu_sta_update(dev, NULL, vif, true,
MT76_STA_INFO_STATE_NONE);
mt7921_mcu_uni_add_beacon_offload(dev, hw, vif, true);
@@ -974,16 +976,16 @@ void mt7921_mac_update_mib_stats(struct mt7921_phy *phy)
mib->tx_amsdu_cnt += val;
}
- for (i = 0, aggr1 = aggr0 + 4; i < 4; i++) {
+ for (i = 0, aggr1 = aggr0 + 8; i < 4; i++) {
u32 val2;
val = mt76_rr(dev, MT_TX_AGG_CNT(0, i));
val2 = mt76_rr(dev, MT_TX_AGG_CNT2(0, i));
- dev->mt76.aggr_stats[aggr0++] += val & 0xffff;
- dev->mt76.aggr_stats[aggr0++] += val >> 16;
- dev->mt76.aggr_stats[aggr1++] += val2 & 0xffff;
- dev->mt76.aggr_stats[aggr1++] += val2 >> 16;
+ phy->mt76->aggr_stats[aggr0++] += val & 0xffff;
+ phy->mt76->aggr_stats[aggr0++] += val >> 16;
+ phy->mt76->aggr_stats[aggr1++] += val2 & 0xffff;
+ phy->mt76->aggr_stats[aggr1++] += val2 >> 16;
}
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
index 7e409ac7d9a8..76ac5069638f 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
@@ -386,6 +386,116 @@ static void mt7921_remove_interface(struct ieee80211_hw *hw,
mt76_packet_id_flush(&dev->mt76, &msta->wcid);
}
+static void mt7921_roc_iter(void *priv, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct mt7921_vif *mvif = (struct mt7921_vif *)vif->drv_priv;
+ struct mt7921_phy *phy = priv;
+
+ mt7921_mcu_abort_roc(phy, mvif, phy->roc_token_id);
+}
+
+void mt7921_roc_work(struct work_struct *work)
+{
+ struct mt7921_phy *phy;
+
+ phy = (struct mt7921_phy *)container_of(work, struct mt7921_phy,
+ roc_work);
+
+ if (!test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state))
+ return;
+
+ mt7921_mutex_acquire(phy->dev);
+ ieee80211_iterate_active_interfaces(phy->mt76->hw,
+ IEEE80211_IFACE_ITER_RESUME_ALL,
+ mt7921_roc_iter, phy);
+ mt7921_mutex_release(phy->dev);
+ ieee80211_remain_on_channel_expired(phy->mt76->hw);
+}
+
+void mt7921_roc_timer(struct timer_list *timer)
+{
+ struct mt7921_phy *phy = from_timer(phy, timer, roc_timer);
+
+ ieee80211_queue_work(phy->mt76->hw, &phy->roc_work);
+}
+
+static int mt7921_abort_roc(struct mt7921_phy *phy, struct mt7921_vif *vif)
+{
+ int err;
+
+ if (!test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state))
+ return 0;
+
+ del_timer_sync(&phy->roc_timer);
+ cancel_work_sync(&phy->roc_work);
+ err = mt7921_mcu_abort_roc(phy, vif, phy->roc_token_id);
+ clear_bit(MT76_STATE_ROC, &phy->mt76->state);
+
+ return err;
+}
+
+static int mt7921_set_roc(struct mt7921_phy *phy,
+ struct mt7921_vif *vif,
+ struct ieee80211_channel *chan,
+ int duration,
+ enum mt7921_roc_req type)
+{
+ int err;
+
+ if (test_and_set_bit(MT76_STATE_ROC, &phy->mt76->state))
+ return -EBUSY;
+
+ phy->roc_grant = false;
+
+ err = mt7921_mcu_set_roc(phy, vif, chan, duration, type,
+ ++phy->roc_token_id);
+ if (err < 0) {
+ clear_bit(MT76_STATE_ROC, &phy->mt76->state);
+ goto out;
+ }
+
+ if (!wait_event_timeout(phy->roc_wait, phy->roc_grant, HZ)) {
+ mt7921_mcu_abort_roc(phy, vif, phy->roc_token_id);
+ clear_bit(MT76_STATE_ROC, &phy->mt76->state);
+ err = -ETIMEDOUT;
+ }
+
+out:
+ return err;
+}
+
+static int mt7921_remain_on_channel(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_channel *chan,
+ int duration,
+ enum ieee80211_roc_type type)
+{
+ struct mt7921_vif *mvif = (struct mt7921_vif *)vif->drv_priv;
+ struct mt7921_phy *phy = mt7921_hw_phy(hw);
+ int err;
+
+ mt7921_mutex_acquire(phy->dev);
+ err = mt7921_set_roc(phy, mvif, chan, duration, MT7921_ROC_REQ_ROC);
+ mt7921_mutex_release(phy->dev);
+
+ return err;
+}
+
+static int mt7921_cancel_remain_on_channel(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct mt7921_vif *mvif = (struct mt7921_vif *)vif->drv_priv;
+ struct mt7921_phy *phy = mt7921_hw_phy(hw);
+ int err;
+
+ mt7921_mutex_acquire(phy->dev);
+ err = mt7921_abort_roc(phy, mvif);
+ mt7921_mutex_release(phy->dev);
+
+ return err;
+}
+
static int mt7921_set_channel(struct mt7921_phy *phy)
{
struct mt7921_dev *dev = phy->dev;
@@ -748,7 +858,7 @@ void mt7921_mac_sta_assoc(struct mt76_dev *mdev, struct ieee80211_vif *vif,
if (vif->type == NL80211_IFTYPE_STATION && !sta->tdls)
mt76_connac_mcu_uni_add_bss(&dev->mphy, vif, &mvif->sta.wcid,
- true);
+ true, mvif->ctx);
mt7921_mac_wtbl_update(dev, msta->wcid.idx,
MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
@@ -780,7 +890,8 @@ void mt7921_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
ewma_rssi_init(&mvif->rssi);
if (!sta->tdls)
mt76_connac_mcu_uni_add_bss(&dev->mphy, vif,
- &mvif->sta.wcid, false);
+ &mvif->sta.wcid, false,
+ mvif->ctx);
}
spin_lock_bh(&dev->sta_poll_lock);
@@ -1075,7 +1186,7 @@ void mt7921_get_et_stats(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
/* Tx ampdu stat */
for (i = 0; i < 15; i++)
- data[ei++] = dev->mt76.aggr_stats[i];
+ data[ei++] = phy->mt76->aggr_stats[i];
data[ei++] = phy->mib.ba_miss_cnt;
@@ -1504,7 +1615,13 @@ static int mt7921_set_sar_specs(struct ieee80211_hw *hw,
int err;
mt7921_mutex_acquire(dev);
+ err = mt7921_mcu_set_clc(dev, dev->mt76.alpha2,
+ dev->country_ie_env);
+ if (err < 0)
+ goto out;
+
err = mt7921_set_tx_sar_pwr(hw, sar);
+out:
mt7921_mutex_release(dev);
return err;
@@ -1534,7 +1651,7 @@ mt7921_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
mt7921_mutex_acquire(dev);
err = mt76_connac_mcu_uni_add_bss(phy->mt76, vif, &mvif->sta.wcid,
- true);
+ true, mvif->ctx);
if (err)
goto out;
@@ -1565,12 +1682,109 @@ mt7921_stop_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
if (err)
goto out;
- mt76_connac_mcu_uni_add_bss(phy->mt76, vif, &mvif->sta.wcid, false);
+ mt76_connac_mcu_uni_add_bss(phy->mt76, vif, &mvif->sta.wcid, false,
+ mvif->ctx);
out:
mt7921_mutex_release(dev);
}
+static int
+mt7921_add_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_chanctx_conf *ctx)
+{
+ return 0;
+}
+
+static void
+mt7921_remove_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_chanctx_conf *ctx)
+{
+}
+
+static void mt7921_ctx_iter(void *priv, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct mt7921_vif *mvif = (struct mt7921_vif *)vif->drv_priv;
+ struct ieee80211_chanctx_conf *ctx = priv;
+
+ if (ctx != mvif->ctx)
+ return;
+
+ mt76_connac_mcu_uni_set_chctx(mvif->phy->mt76, &mvif->mt76, ctx);
+}
+
+static void
+mt7921_change_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_chanctx_conf *ctx,
+ u32 changed)
+{
+ struct mt7921_phy *phy = mt7921_hw_phy(hw);
+
+ mt7921_mutex_acquire(phy->dev);
+ ieee80211_iterate_active_interfaces(phy->mt76->hw,
+ IEEE80211_IFACE_ITER_ACTIVE,
+ mt7921_ctx_iter, ctx);
+ mt7921_mutex_release(phy->dev);
+}
+
+static int
+mt7921_assign_vif_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link_conf,
+ struct ieee80211_chanctx_conf *ctx)
+{
+ struct mt7921_vif *mvif = (struct mt7921_vif *)vif->drv_priv;
+ struct mt7921_dev *dev = mt7921_hw_dev(hw);
+
+ mutex_lock(&dev->mt76.mutex);
+ mvif->ctx = ctx;
+ mutex_unlock(&dev->mt76.mutex);
+
+ return 0;
+}
+
+static void
+mt7921_unassign_vif_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *link_conf,
+ struct ieee80211_chanctx_conf *ctx)
+{
+ struct mt7921_vif *mvif = (struct mt7921_vif *)vif->drv_priv;
+ struct mt7921_dev *dev = mt7921_hw_dev(hw);
+
+ mutex_lock(&dev->mt76.mutex);
+ mvif->ctx = NULL;
+ mutex_unlock(&dev->mt76.mutex);
+}
+
+static void mt7921_mgd_prepare_tx(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_prep_tx_info *info)
+{
+ struct mt7921_vif *mvif = (struct mt7921_vif *)vif->drv_priv;
+ struct mt7921_dev *dev = mt7921_hw_dev(hw);
+ u16 duration = info->duration ? info->duration :
+ jiffies_to_msecs(HZ);
+
+ mt7921_mutex_acquire(dev);
+ mt7921_set_roc(mvif->phy, mvif, mvif->ctx->def.chan, duration,
+ MT7921_ROC_REQ_JOIN);
+ mt7921_mutex_release(dev);
+}
+
+static void mt7921_mgd_complete_tx(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_prep_tx_info *info)
+{
+ struct mt7921_vif *mvif = (struct mt7921_vif *)vif->drv_priv;
+ struct mt7921_dev *dev = mt7921_hw_dev(hw);
+
+ mt7921_mutex_acquire(dev);
+ mt7921_abort_roc(mvif->phy, mvif);
+ mt7921_mutex_release(dev);
+}
+
const struct ieee80211_ops mt7921_ops = {
.tx = mt7921_tx,
.start = mt7921_start,
@@ -1621,6 +1835,15 @@ const struct ieee80211_ops mt7921_ops = {
#endif /* CONFIG_PM */
.flush = mt7921_flush,
.set_sar_specs = mt7921_set_sar_specs,
+ .remain_on_channel = mt7921_remain_on_channel,
+ .cancel_remain_on_channel = mt7921_cancel_remain_on_channel,
+ .add_chanctx = mt7921_add_chanctx,
+ .remove_chanctx = mt7921_remove_chanctx,
+ .change_chanctx = mt7921_change_chanctx,
+ .assign_vif_chanctx = mt7921_assign_vif_chanctx,
+ .unassign_vif_chanctx = mt7921_unassign_vif_chanctx,
+ .mgd_prepare_tx = mt7921_mgd_prepare_tx,
+ .mgd_complete_tx = mt7921_mgd_complete_tx,
};
EXPORT_SYMBOL_GPL(mt7921_ops);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c
index 67bf92969a7b..fb9c0f66cb27 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c
@@ -155,6 +155,29 @@ void mt7921_mcu_set_suspend_iter(void *priv, u8 *mac, struct ieee80211_vif *vif)
#endif /* CONFIG_PM */
static void
+mt7921_mcu_uni_roc_event(struct mt7921_dev *dev, struct sk_buff *skb)
+{
+ struct mt7921_roc_grant_tlv *grant;
+ struct mt76_connac2_mcu_rxd *rxd;
+ int duration;
+
+ rxd = (struct mt76_connac2_mcu_rxd *)skb->data;
+ grant = (struct mt7921_roc_grant_tlv *)(rxd->tlv + 4);
+
+ /* should never happen */
+ WARN_ON_ONCE((le16_to_cpu(grant->tag) != UNI_EVENT_ROC_GRANT));
+
+ if (grant->reqtype == MT7921_ROC_REQ_ROC)
+ ieee80211_ready_on_channel(dev->mt76.phy.hw);
+
+ dev->phy.roc_grant = true;
+ wake_up(&dev->phy.roc_wait);
+ duration = le32_to_cpu(grant->max_interval);
+ mod_timer(&dev->phy.roc_timer,
+ round_jiffies_up(jiffies + msecs_to_jiffies(duration)));
+}
+
+static void
mt7921_mcu_scan_event(struct mt7921_dev *dev, struct sk_buff *skb)
{
struct mt76_phy *mphy = &dev->mt76.phy;
@@ -200,20 +223,6 @@ mt7921_mcu_connection_loss_event(struct mt7921_dev *dev, struct sk_buff *skb)
}
static void
-mt7921_mcu_bss_event(struct mt7921_dev *dev, struct sk_buff *skb)
-{
- struct mt76_phy *mphy = &dev->mt76.phy;
- struct mt76_connac_mcu_bss_event *event;
-
- skb_pull(skb, sizeof(struct mt76_connac2_mcu_rxd));
- event = (struct mt76_connac_mcu_bss_event *)skb->data;
- if (event->is_absent)
- ieee80211_stop_queues(mphy->hw);
- else
- ieee80211_wake_queues(mphy->hw);
-}
-
-static void
mt7921_mcu_debug_msg_event(struct mt7921_dev *dev, struct sk_buff *skb)
{
struct mt7921_debug_msg {
@@ -279,9 +288,6 @@ mt7921_mcu_rx_unsolicited_event(struct mt7921_dev *dev, struct sk_buff *skb)
case MCU_EVENT_SCAN_DONE:
mt7921_mcu_scan_event(dev, skb);
return;
- case MCU_EVENT_BSS_ABSENCE:
- mt7921_mcu_bss_event(dev, skb);
- break;
case MCU_EVENT_DBG_MSG:
mt7921_mcu_debug_msg_event(dev, skb);
break;
@@ -302,6 +308,24 @@ mt7921_mcu_rx_unsolicited_event(struct mt7921_dev *dev, struct sk_buff *skb)
dev_kfree_skb(skb);
}
+static void
+mt7921_mcu_uni_rx_unsolicited_event(struct mt7921_dev *dev,
+ struct sk_buff *skb)
+{
+ struct mt76_connac2_mcu_rxd *rxd;
+
+ rxd = (struct mt76_connac2_mcu_rxd *)skb->data;
+
+ switch (rxd->eid) {
+ case MCU_UNI_EVENT_ROC:
+ mt7921_mcu_uni_roc_event(dev, skb);
+ break;
+ default:
+ break;
+ }
+ dev_kfree_skb(skb);
+}
+
void mt7921_mcu_rx_event(struct mt7921_dev *dev, struct sk_buff *skb)
{
struct mt76_connac2_mcu_rxd *rxd;
@@ -311,6 +335,11 @@ void mt7921_mcu_rx_event(struct mt7921_dev *dev, struct sk_buff *skb)
rxd = (struct mt76_connac2_mcu_rxd *)skb->data;
+ if (rxd->option & MCU_UNI_CMD_UNSOLICITED_EVENT) {
+ mt7921_mcu_uni_rx_unsolicited_event(dev, skb);
+ return;
+ }
+
if (rxd->eid == 0x6) {
mt76_mcu_rx_event(&dev->mt76, skb);
return;
@@ -319,7 +348,6 @@ void mt7921_mcu_rx_event(struct mt7921_dev *dev, struct sk_buff *skb)
if (rxd->ext_eid == MCU_EXT_EVENT_RATE_REPORT ||
rxd->eid == MCU_EVENT_BSS_BEACON_LOSS ||
rxd->eid == MCU_EVENT_SCHED_SCAN_DONE ||
- rxd->eid == MCU_EVENT_BSS_ABSENCE ||
rxd->eid == MCU_EVENT_SCAN_DONE ||
rxd->eid == MCU_EVENT_TX_DONE ||
rxd->eid == MCU_EVENT_DBG_MSG ||
@@ -636,6 +664,103 @@ int mt7921_mcu_set_tx(struct mt7921_dev *dev, struct ieee80211_vif *vif)
&req_mu, sizeof(req_mu), false);
}
+int mt7921_mcu_set_roc(struct mt7921_phy *phy, struct mt7921_vif *vif,
+ struct ieee80211_channel *chan, int duration,
+ enum mt7921_roc_req type, u8 token_id)
+{
+ int center_ch = ieee80211_frequency_to_channel(chan->center_freq);
+ struct mt7921_dev *dev = phy->dev;
+ struct {
+ struct {
+ u8 rsv[4];
+ } __packed hdr;
+ struct roc_acquire_tlv {
+ __le16 tag;
+ __le16 len;
+ u8 bss_idx;
+ u8 tokenid;
+ u8 control_channel;
+ u8 sco;
+ u8 band;
+ u8 bw;
+ u8 center_chan;
+ u8 center_chan2;
+ u8 bw_from_ap;
+ u8 center_chan_from_ap;
+ u8 center_chan2_from_ap;
+ u8 reqtype;
+ __le32 maxinterval;
+ u8 dbdcband;
+ u8 rsv[3];
+ } __packed roc;
+ } __packed req = {
+ .roc = {
+ .tag = cpu_to_le16(UNI_ROC_ACQUIRE),
+ .len = cpu_to_le16(sizeof(struct roc_acquire_tlv)),
+ .tokenid = token_id,
+ .reqtype = type,
+ .maxinterval = cpu_to_le32(duration),
+ .bss_idx = vif->mt76.idx,
+ .control_channel = chan->hw_value,
+ .bw = CMD_CBW_20MHZ,
+ .bw_from_ap = CMD_CBW_20MHZ,
+ .center_chan = center_ch,
+ .center_chan_from_ap = center_ch,
+ .dbdcband = 0xff, /* auto */
+ },
+ };
+
+ if (chan->hw_value < center_ch)
+ req.roc.sco = 1; /* SCA */
+ else if (chan->hw_value > center_ch)
+ req.roc.sco = 3; /* SCB */
+
+ switch (chan->band) {
+ case NL80211_BAND_6GHZ:
+ req.roc.band = 3;
+ break;
+ case NL80211_BAND_5GHZ:
+ req.roc.band = 2;
+ break;
+ default:
+ req.roc.band = 1;
+ break;
+ }
+
+ return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(ROC),
+ &req, sizeof(req), false);
+}
+
+int mt7921_mcu_abort_roc(struct mt7921_phy *phy, struct mt7921_vif *vif,
+ u8 token_id)
+{
+ struct mt7921_dev *dev = phy->dev;
+ struct {
+ struct {
+ u8 rsv[4];
+ } __packed hdr;
+ struct roc_abort_tlv {
+ __le16 tag;
+ __le16 len;
+ u8 bss_idx;
+ u8 tokenid;
+ u8 dbdcband;
+ u8 rsv[5];
+ } __packed abort;
+ } __packed req = {
+ .abort = {
+ .tag = cpu_to_le16(UNI_ROC_ABORT),
+ .len = cpu_to_le16(sizeof(struct roc_abort_tlv)),
+ .tokenid = token_id,
+ .bss_idx = vif->mt76.idx,
+ .dbdcband = 0xff, /* auto*/
+ },
+ };
+
+ return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(ROC),
+ &req, sizeof(req), false);
+}
+
int mt7921_mcu_set_chan_info(struct mt7921_phy *phy, int cmd)
{
struct mt7921_dev *dev = phy->dev;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h b/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h
index eaba114a9c7e..15d6b7fe1c6c 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h
@@ -32,6 +32,9 @@
#define MT7921_MCU_INIT_RETRY_COUNT 10
#define MT7921_WFSYS_INIT_RETRY_COUNT 2
+#define MT7921_FW_TAG_FEATURE 4
+#define MT7921_FW_CAP_CNM BIT(7)
+
#define MT7921_FIRMWARE_WM "mediatek/WIFI_RAM_CODE_MT7961_1.bin"
#define MT7921_ROM_PATCH "mediatek/WIFI_MT7961_patch_mcu_1_2_hdr.bin"
@@ -53,6 +56,55 @@
#define MT7921_SDIO_HDR_TX_BYTES GENMASK(15, 0)
#define MT7921_SDIO_HDR_PKT_TYPE GENMASK(17, 16)
+#define MCU_UNI_EVENT_ROC 0x27
+
+enum {
+ UNI_ROC_ACQUIRE,
+ UNI_ROC_ABORT,
+ UNI_ROC_NUM
+};
+
+enum mt7921_roc_req {
+ MT7921_ROC_REQ_JOIN,
+ MT7921_ROC_REQ_ROC,
+ MT7921_ROC_REQ_NUM
+};
+
+enum {
+ UNI_EVENT_ROC_GRANT = 0,
+ UNI_EVENT_ROC_TAG_NUM
+};
+
+struct mt7921_realease_info {
+ __le16 len;
+ u8 pad_len;
+ u8 tag;
+} __packed;
+
+struct mt7921_fw_features {
+ u8 segment;
+ u8 data;
+ u8 rsv[14];
+} __packed;
+
+struct mt7921_roc_grant_tlv {
+ __le16 tag;
+ __le16 len;
+ u8 bss_idx;
+ u8 tokenid;
+ u8 status;
+ u8 primarychannel;
+ u8 rfsco;
+ u8 rfband;
+ u8 channelwidth;
+ u8 centerfreqseg1;
+ u8 centerfreqseg2;
+ u8 reqtype;
+ u8 dbdcband;
+ u8 rsv[1];
+ __le32 max_interval;
+} __packed;
+
enum mt7921_sdio_pkt_type {
MT7921_SDIO_TXD,
MT7921_SDIO_DATA,
@@ -119,6 +171,7 @@ struct mt7921_vif {
struct ewma_rssi rssi;
struct ieee80211_tx_queue_params queue_params[IEEE80211_NUM_ACS];
+ struct ieee80211_chanctx_conf *ctx;
};
struct mib_stats {
@@ -171,7 +224,7 @@ struct mt7921_clc {
u8 type;
u8 rsv[8];
u8 data[];
-};
+} __packed;
struct mt7921_phy {
struct mt76_phy *mt76;
@@ -200,6 +253,12 @@ struct mt7921_phy {
#endif
struct mt7921_clc *clc[MT7921_CLC_MAX_NUM];
+
+ struct work_struct roc_work;
+ struct timer_list roc_timer;
+ wait_queue_head_t roc_wait;
+ u8 roc_token_id;
+ bool roc_grant;
};
#define mt7921_init_reset(dev) ((dev)->hif_ops->init_reset(dev))
@@ -236,6 +295,7 @@ struct mt7921_dev {
struct work_struct init_work;
u8 fw_debug;
+ u8 fw_features;
struct mt76_connac_pm pm;
struct mt76_connac_coredump coredump;
@@ -244,6 +304,8 @@ struct mt7921_dev {
struct work_struct ipv6_ns_work;
/* IPv6 addresses for WoWLAN */
struct sk_buff_head ipv6_ns_list;
+
+ enum environment_cap country_ie_env;
};
enum {
@@ -408,7 +470,7 @@ void mt7921_tx_worker(struct mt76_worker *w);
void mt7921_tx_token_put(struct mt7921_dev *dev);
bool mt7921_rx_check(struct mt76_dev *mdev, void *data, int len);
void mt7921_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
- struct sk_buff *skb);
+ struct sk_buff *skb, u32 *info);
void mt7921_sta_ps(struct mt76_dev *mdev, struct ieee80211_sta *sta, bool ps);
void mt7921_stats_work(struct work_struct *work);
void mt7921_set_stream_he_caps(struct mt7921_phy *phy);
@@ -425,6 +487,8 @@ int mt7921_mcu_uni_rx_ba(struct mt7921_dev *dev,
struct ieee80211_ampdu_params *params,
bool enable);
void mt7921_scan_work(struct work_struct *work);
+void mt7921_roc_work(struct work_struct *work);
+void mt7921_roc_timer(struct timer_list *timer);
int mt7921_mcu_uni_bss_ps(struct mt7921_dev *dev, struct ieee80211_vif *vif);
int mt7921_mcu_drv_pmctrl(struct mt7921_dev *dev);
int mt7921_mcu_fw_pmctrl(struct mt7921_dev *dev);
@@ -508,4 +572,10 @@ int mt7921_set_tx_sar_pwr(struct ieee80211_hw *hw,
int mt7921_mcu_set_clc(struct mt7921_dev *dev, u8 *alpha2,
enum environment_cap env_cap);
+int mt7921_mcu_set_roc(struct mt7921_phy *phy, struct mt7921_vif *vif,
+ struct ieee80211_channel *chan, int duration,
+ enum mt7921_roc_req type, u8 token_id);
+int mt7921_mcu_abort_roc(struct mt7921_phy *phy, struct mt7921_vif *vif,
+ u8 token_id);
+u8 mt7921_check_offload_capability(struct device *dev, const char *fw_wm);
#endif
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c
index 8a53d8f286db..cb72ded37256 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c
@@ -13,10 +13,14 @@
#include "../trace.h"
static const struct pci_device_id mt7921_pci_device_table[] = {
- { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7961) },
- { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7922) },
- { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x0608) },
- { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x0616) },
+ { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7961),
+ .driver_data = (kernel_ulong_t)MT7921_FIRMWARE_WM },
+ { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7922),
+ .driver_data = (kernel_ulong_t)MT7922_FIRMWARE_WM },
+ { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x0608),
+ .driver_data = (kernel_ulong_t)MT7921_FIRMWARE_WM },
+ { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x0616),
+ .driver_data = (kernel_ulong_t)MT7921_FIRMWARE_WM },
{ },
};
@@ -228,7 +232,8 @@ static int mt7921_pci_probe(struct pci_dev *pdev,
static const struct mt76_driver_ops drv_ops = {
/* txwi_size = txd size + txp size */
.txwi_size = MT_TXD_SIZE + sizeof(struct mt76_connac_hw_txp),
- .drv_flags = MT_DRV_TXWI_NO_FREE | MT_DRV_HW_MGMT_TXQ,
+ .drv_flags = MT_DRV_TXWI_NO_FREE | MT_DRV_HW_MGMT_TXQ |
+ MT_DRV_AMSDU_OFFLOAD,
.survey_flags = SURVEY_INFO_TIME_TX |
SURVEY_INFO_TIME_RX |
SURVEY_INFO_TIME_BSS_RX,
@@ -252,9 +257,11 @@ static int mt7921_pci_probe(struct pci_dev *pdev,
.fw_own = mt7921e_mcu_fw_pmctrl,
};
+ struct ieee80211_ops *ops;
struct mt76_bus_ops *bus_ops;
struct mt7921_dev *dev;
struct mt76_dev *mdev;
+ u8 features;
int ret;
ret = pcim_enable_device(pdev);
@@ -278,8 +285,28 @@ static int mt7921_pci_probe(struct pci_dev *pdev,
if (mt7921_disable_aspm)
mt76_pci_disable_aspm(pdev);
- mdev = mt76_alloc_device(&pdev->dev, sizeof(*dev), &mt7921_ops,
- &drv_ops);
+ features = mt7921_check_offload_capability(&pdev->dev, (const char *)
+ id->driver_data);
+ ops = devm_kmemdup(&pdev->dev, &mt7921_ops, sizeof(mt7921_ops),
+ GFP_KERNEL);
+ if (!ops) {
+ ret = -ENOMEM;
+ goto err_free_pci_vec;
+ }
+
+ if (!(features & MT7921_FW_CAP_CNM)) {
+ ops->remain_on_channel = NULL;
+ ops->cancel_remain_on_channel = NULL;
+ ops->add_chanctx = NULL;
+ ops->remove_chanctx = NULL;
+ ops->change_chanctx = NULL;
+ ops->assign_vif_chanctx = NULL;
+ ops->unassign_vif_chanctx = NULL;
+ ops->mgd_prepare_tx = NULL;
+ ops->mgd_complete_tx = NULL;
+ }
+
+ mdev = mt76_alloc_device(&pdev->dev, sizeof(*dev), ops, &drv_ops);
if (!mdev) {
ret = -ENOMEM;
goto err_free_pci_vec;
@@ -288,8 +315,8 @@ static int mt7921_pci_probe(struct pci_dev *pdev,
pci_set_drvdata(pdev, mdev);
dev = container_of(mdev, struct mt7921_dev, mt76);
+ dev->fw_features = features;
dev->hif_ops = &mt7921_pcie_ops;
-
mt76_mmio_init(&dev->mt76, pcim_iomap_table(pdev)[0]);
tasklet_init(&dev->irq_tasklet, mt7921_irq_tasklet, (unsigned long)dev);
@@ -480,6 +507,21 @@ failed:
return err;
}
+static void mt7921_pci_shutdown(struct pci_dev *pdev)
+{
+ struct mt76_dev *mdev = pci_get_drvdata(pdev);
+ struct mt7921_dev *dev = container_of(mdev, struct mt7921_dev, mt76);
+ struct mt76_connac_pm *pm = &dev->pm;
+
+ cancel_delayed_work_sync(&pm->ps_work);
+ cancel_work_sync(&pm->wake_work);
+
+ /* chip cleanup before reboot */
+ mt7921_mcu_drv_pmctrl(dev);
+ mt7921_dma_cleanup(dev);
+ mt7921_wfsys_reset(dev);
+}
+
static DEFINE_SIMPLE_DEV_PM_OPS(mt7921_pm_ops, mt7921_pci_suspend, mt7921_pci_resume);
static struct pci_driver mt7921_pci_driver = {
@@ -487,6 +529,7 @@ static struct pci_driver mt7921_pci_driver = {
.id_table = mt7921_pci_device_table,
.probe = mt7921_pci_probe,
.remove = mt7921_pci_remove,
+ .shutdown = mt7921_pci_shutdown,
.driver.pm = pm_sleep_ptr(&mt7921_pm_ops),
};
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c b/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c
index 3b25a06fd946..8ce4252b8ae7 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c
@@ -17,7 +17,8 @@
#include "mcu.h"
static const struct sdio_device_id mt7921s_table[] = {
- { SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, 0x7901) },
+ { SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, 0x7901),
+ .driver_data = (kernel_ulong_t)MT7921_FIRMWARE_WM },
{ } /* Terminating entry */
};
@@ -89,6 +90,7 @@ static int mt7921s_probe(struct sdio_func *func,
{
static const struct mt76_driver_ops drv_ops = {
.txwi_size = MT_SDIO_TXD_SIZE,
+ .drv_flags = MT_DRV_AMSDU_OFFLOAD,
.survey_flags = SURVEY_INFO_TIME_TX |
SURVEY_INFO_TIME_RX |
SURVEY_INFO_TIME_BSS_RX,
@@ -121,18 +123,39 @@ static int mt7921s_probe(struct sdio_func *func,
.fw_own = mt7921s_mcu_fw_pmctrl,
};
+ struct ieee80211_ops *ops;
struct mt7921_dev *dev;
struct mt76_dev *mdev;
+ u8 features;
int ret;
- mdev = mt76_alloc_device(&func->dev, sizeof(*dev), &mt7921_ops,
- &drv_ops);
+ features = mt7921_check_offload_capability(&func->dev, (const char *)
+ id->driver_data);
+
+ ops = devm_kmemdup(&func->dev, &mt7921_ops, sizeof(mt7921_ops),
+ GFP_KERNEL);
+ if (!ops)
+ return -ENOMEM;
+
+ if (!(features & MT7921_FW_CAP_CNM)) {
+ ops->remain_on_channel = NULL;
+ ops->cancel_remain_on_channel = NULL;
+ ops->add_chanctx = NULL;
+ ops->remove_chanctx = NULL;
+ ops->change_chanctx = NULL;
+ ops->assign_vif_chanctx = NULL;
+ ops->unassign_vif_chanctx = NULL;
+ ops->mgd_prepare_tx = NULL;
+ ops->mgd_complete_tx = NULL;
+ }
+
+ mdev = mt76_alloc_device(&func->dev, sizeof(*dev), ops, &drv_ops);
if (!mdev)
return -ENOMEM;
dev = container_of(mdev, struct mt7921_dev, mt76);
+ dev->fw_features = features;
dev->hif_ops = &mt7921_sdio_ops;
-
sdio_set_drvdata(func, dev);
ret = mt76s_init(mdev, func, &mt7921s_ops);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/usb.c b/drivers/net/wireless/mediatek/mt76/mt7921/usb.c
index 29c0ee330dbe..5321d20dcdcb 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/usb.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/usb.c
@@ -13,7 +13,8 @@
#include "mac.h"
static const struct usb_device_id mt7921u_device_table[] = {
- { USB_DEVICE_AND_INTERFACE_INFO(0x0e8d, 0x7961, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x0e8d, 0x7961, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)MT7921_FIRMWARE_WM },
{ },
};
@@ -170,7 +171,8 @@ static int mt7921u_probe(struct usb_interface *usb_intf,
{
static const struct mt76_driver_ops drv_ops = {
.txwi_size = MT_SDIO_TXD_SIZE,
- .drv_flags = MT_DRV_RX_DMA_HDR | MT_DRV_HW_MGMT_TXQ,
+ .drv_flags = MT_DRV_RX_DMA_HDR | MT_DRV_HW_MGMT_TXQ |
+ MT_DRV_AMSDU_OFFLOAD,
.survey_flags = SURVEY_INFO_TIME_TX |
SURVEY_INFO_TIME_RX |
SURVEY_INFO_TIME_BSS_RX,
@@ -203,13 +205,28 @@ static int mt7921u_probe(struct usb_interface *usb_intf,
struct ieee80211_hw *hw;
struct mt7921_dev *dev;
struct mt76_dev *mdev;
+ u8 features;
int ret;
+ features = mt7921_check_offload_capability(&usb_intf->dev, (const char *)
+ id->driver_info);
ops = devm_kmemdup(&usb_intf->dev, &mt7921_ops, sizeof(mt7921_ops),
GFP_KERNEL);
if (!ops)
return -ENOMEM;
+ if (!(features & MT7921_FW_CAP_CNM)) {
+ ops->remain_on_channel = NULL;
+ ops->cancel_remain_on_channel = NULL;
+ ops->add_chanctx = NULL;
+ ops->remove_chanctx = NULL;
+ ops->change_chanctx = NULL;
+ ops->assign_vif_chanctx = NULL;
+ ops->unassign_vif_chanctx = NULL;
+ ops->mgd_prepare_tx = NULL;
+ ops->mgd_complete_tx = NULL;
+ }
+
ops->stop = mt7921u_stop;
mdev = mt76_alloc_device(&usb_intf->dev, sizeof(*dev), ops, &drv_ops);
@@ -217,6 +234,7 @@ static int mt7921u_probe(struct usb_interface *usb_intf,
return -ENOMEM;
dev = container_of(mdev, struct mt7921_dev, mt76);
+ dev->fw_features = features;
dev->hif_ops = &hif_ops;
udev = usb_get_dev(udev);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/Kconfig b/drivers/net/wireless/mediatek/mt76/mt7996/Kconfig
new file mode 100644
index 000000000000..5c5fc569e6d5
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/Kconfig
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: ISC
+config MT7996E
+ tristate "MediaTek MT7996 (PCIe) support"
+ select MT76_CONNAC_LIB
+ depends on MAC80211
+ depends on PCI
+ help
+ This adds support for MT7996-based wireless PCIe devices,
+ which support concurrent tri-band operation at 6GHz, 5GHz,
+ and 2.4GHz IEEE 802.11be 4x4:4SS 4096-QAM, 320MHz channels.
+
+ To compile this driver as a module, choose M here.
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/Makefile b/drivers/net/wireless/mediatek/mt76/mt7996/Makefile
new file mode 100644
index 000000000000..bcb9a3c53149
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: ISC
+
+obj-$(CONFIG_MT7996E) += mt7996e.o
+
+mt7996e-y := pci.o init.o dma.o eeprom.o main.o mcu.o mac.o \
+ debugfs.o mmio.o
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c
new file mode 100644
index 000000000000..2e4a8909b9e8
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c
@@ -0,0 +1,851 @@
+// SPDX-License-Identifier: ISC
+/*
+ * Copyright (C) 2022 MediaTek Inc.
+ */
+
+#include <linux/relay.h>
+#include "mt7996.h"
+#include "eeprom.h"
+#include "mcu.h"
+#include "mac.h"
+
+#define FW_BIN_LOG_MAGIC 0x44d9c99a
+
+/** global debugfs **/
+
+struct hw_queue_map {
+ const char *name;
+ u8 index;
+ u8 pid;
+ u8 qid;
+};
+
+static int
+mt7996_implicit_txbf_set(void *data, u64 val)
+{
+ struct mt7996_dev *dev = data;
+
+ /* The existing connected stations shall reconnect to apply
+ * new implicit txbf configuration.
+ */
+ dev->ibf = !!val;
+
+ return mt7996_mcu_set_txbf(dev, BF_HW_EN_UPDATE);
+}
+
+static int
+mt7996_implicit_txbf_get(void *data, u64 *val)
+{
+ struct mt7996_dev *dev = data;
+
+ *val = dev->ibf;
+
+ return 0;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(fops_implicit_txbf, mt7996_implicit_txbf_get,
+ mt7996_implicit_txbf_set, "%lld\n");
+
+/* test knob of system error recovery */
+static ssize_t
+mt7996_fw_ser_set(struct file *file, const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct mt7996_phy *phy = file->private_data;
+ struct mt7996_dev *dev = phy->dev;
+ u8 band_idx = phy->mt76->band_idx;
+ char buf[16];
+ int ret = 0;
+ u16 val;
+
+ if (count >= sizeof(buf))
+ return -EINVAL;
+
+ if (copy_from_user(buf, user_buf, count))
+ return -EFAULT;
+
+ if (count && buf[count - 1] == '\n')
+ buf[count - 1] = '\0';
+ else
+ buf[count] = '\0';
+
+ if (kstrtou16(buf, 0, &val))
+ return -EINVAL;
+
+ switch (val) {
+ case SER_SET_RECOVER_L1:
+ case SER_SET_RECOVER_L2:
+ case SER_SET_RECOVER_L3_RX_ABORT:
+ case SER_SET_RECOVER_L3_TX_ABORT:
+ case SER_SET_RECOVER_L3_TX_DISABLE:
+ case SER_SET_RECOVER_L3_BF:
+ ret = mt7996_mcu_set_ser(dev, SER_ENABLE, BIT(val), band_idx);
+ if (ret)
+ return ret;
+
+ ret = mt7996_mcu_set_ser(dev, SER_RECOVER, val, band_idx);
+ break;
+ default:
+ break;
+ }
+
+ return ret ? ret : count;
+}
+
+static const struct file_operations mt7996_fw_ser_ops = {
+ .write = mt7996_fw_ser_set,
+ /* TODO: ser read */
+ .open = simple_open,
+ .llseek = default_llseek,
+};
+
+static int
+mt7996_radar_trigger(void *data, u64 val)
+{
+ struct mt7996_dev *dev = data;
+
+ if (val > MT_RX_SEL2)
+ return -EINVAL;
+
+ return mt7996_mcu_rdd_cmd(dev, RDD_RADAR_EMULATE,
+ val, 0, 0);
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(fops_radar_trigger, NULL,
+ mt7996_radar_trigger, "%lld\n");
+
+static int
+mt7996_rdd_monitor(struct seq_file *s, void *data)
+{
+ struct mt7996_dev *dev = dev_get_drvdata(s->private);
+ struct cfg80211_chan_def *chandef = &dev->rdd2_chandef;
+ const char *bw;
+ int ret = 0;
+
+ mutex_lock(&dev->mt76.mutex);
+
+ if (!cfg80211_chandef_valid(chandef)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (!dev->rdd2_phy) {
+ seq_puts(s, "not running\n");
+ goto out;
+ }
+
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_40:
+ bw = "40";
+ break;
+ case NL80211_CHAN_WIDTH_80:
+ bw = "80";
+ break;
+ case NL80211_CHAN_WIDTH_160:
+ bw = "160";
+ break;
+ case NL80211_CHAN_WIDTH_80P80:
+ bw = "80P80";
+ break;
+ default:
+ bw = "20";
+ break;
+ }
+
+ seq_printf(s, "channel %d (%d MHz) width %s MHz center1: %d MHz\n",
+ chandef->chan->hw_value, chandef->chan->center_freq,
+ bw, chandef->center_freq1);
+out:
+ mutex_unlock(&dev->mt76.mutex);
+
+ return ret;
+}
+
+static int
+mt7996_fw_debug_wm_set(void *data, u64 val)
+{
+ struct mt7996_dev *dev = data;
+ enum {
+ DEBUG_TXCMD = 62,
+ DEBUG_CMD_RPT_TX,
+ DEBUG_CMD_RPT_TRIG,
+ DEBUG_SPL,
+ DEBUG_RPT_RX,
+ DEBUG_RPT_RA = 68,
+ } debug;
+ bool tx, rx, en;
+ int ret;
+
+ dev->fw_debug_wm = val ? MCU_FW_LOG_TO_HOST : 0;
+
+ if (dev->fw_debug_bin)
+ val = MCU_FW_LOG_RELAY;
+ else
+ val = dev->fw_debug_wm;
+
+ tx = dev->fw_debug_wm || (dev->fw_debug_bin & BIT(1));
+ rx = dev->fw_debug_wm || (dev->fw_debug_bin & BIT(2));
+ en = dev->fw_debug_wm || (dev->fw_debug_bin & BIT(0));
+
+ ret = mt7996_mcu_fw_log_2_host(dev, MCU_FW_LOG_WM, val);
+ if (ret)
+ return ret;
+
+ for (debug = DEBUG_TXCMD; debug <= DEBUG_RPT_RA; debug++) {
+ if (debug == 67)
+ continue;
+
+ if (debug == DEBUG_RPT_RX)
+ val = en && rx;
+ else
+ val = en && tx;
+
+ ret = mt7996_mcu_fw_dbg_ctrl(dev, debug, val);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int
+mt7996_fw_debug_wm_get(void *data, u64 *val)
+{
+ struct mt7996_dev *dev = data;
+
+ *val = dev->fw_debug_wm;
+
+ return 0;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(fops_fw_debug_wm, mt7996_fw_debug_wm_get,
+ mt7996_fw_debug_wm_set, "%lld\n");
+
+static int
+mt7996_fw_debug_wa_set(void *data, u64 val)
+{
+ struct mt7996_dev *dev = data;
+ int ret;
+
+ dev->fw_debug_wa = val ? MCU_FW_LOG_TO_HOST : 0;
+
+ ret = mt7996_mcu_fw_log_2_host(dev, MCU_FW_LOG_WA, dev->fw_debug_wa);
+ if (ret)
+ return ret;
+
+ return mt7996_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(SET), MCU_WA_PARAM_PDMA_RX,
+ !!dev->fw_debug_wa, 0);
+}
+
+static int
+mt7996_fw_debug_wa_get(void *data, u64 *val)
+{
+ struct mt7996_dev *dev = data;
+
+ *val = dev->fw_debug_wa;
+
+ return 0;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(fops_fw_debug_wa, mt7996_fw_debug_wa_get,
+ mt7996_fw_debug_wa_set, "%lld\n");
+
+static struct dentry *
+create_buf_file_cb(const char *filename, struct dentry *parent, umode_t mode,
+ struct rchan_buf *buf, int *is_global)
+{
+ struct dentry *f;
+
+ f = debugfs_create_file("fwlog_data", mode, parent, buf,
+ &relay_file_operations);
+ if (IS_ERR(f))
+ return NULL;
+
+ *is_global = 1;
+
+ return f;
+}
+
+static int
+remove_buf_file_cb(struct dentry *f)
+{
+ debugfs_remove(f);
+
+ return 0;
+}
+
+static int
+mt7996_fw_debug_bin_set(void *data, u64 val)
+{
+ static struct rchan_callbacks relay_cb = {
+ .create_buf_file = create_buf_file_cb,
+ .remove_buf_file = remove_buf_file_cb,
+ };
+ struct mt7996_dev *dev = data;
+
+ if (!dev->relay_fwlog)
+ dev->relay_fwlog = relay_open("fwlog_data", dev->debugfs_dir,
+ 1500, 512, &relay_cb, NULL);
+ if (!dev->relay_fwlog)
+ return -ENOMEM;
+
+ dev->fw_debug_bin = val;
+
+ relay_reset(dev->relay_fwlog);
+
+ return mt7996_fw_debug_wm_set(dev, dev->fw_debug_wm);
+}
+
+static int
+mt7996_fw_debug_bin_get(void *data, u64 *val)
+{
+ struct mt7996_dev *dev = data;
+
+ *val = dev->fw_debug_bin;
+
+ return 0;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(fops_fw_debug_bin, mt7996_fw_debug_bin_get,
+ mt7996_fw_debug_bin_set, "%lld\n");
+
+static int
+mt7996_fw_util_wa_show(struct seq_file *file, void *data)
+{
+ struct mt7996_dev *dev = file->private;
+
+ if (dev->fw_debug_wa)
+ return mt7996_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(QUERY),
+ MCU_WA_PARAM_CPU_UTIL, 0, 0);
+
+ return 0;
+}
+
+DEFINE_SHOW_ATTRIBUTE(mt7996_fw_util_wa);
+
+static void
+mt7996_ampdu_stat_read_phy(struct mt7996_phy *phy, struct seq_file *file)
+{
+ struct mt7996_dev *dev = phy->dev;
+ int bound[15], range[8], i;
+ u8 band_idx = phy->mt76->band_idx;
+
+ /* Tx ampdu stat */
+ for (i = 0; i < ARRAY_SIZE(range); i++)
+ range[i] = mt76_rr(dev, MT_MIB_ARNG(band_idx, i));
+
+ for (i = 0; i < ARRAY_SIZE(bound); i++)
+ bound[i] = MT_MIB_ARNCR_RANGE(range[i / 2], i % 2) + 1;
+
+ seq_printf(file, "\nPhy %s, Phy band %d\n",
+ wiphy_name(phy->mt76->hw->wiphy), band_idx);
+
+ seq_printf(file, "Length: %8d | ", bound[0]);
+ for (i = 0; i < ARRAY_SIZE(bound) - 1; i++)
+ seq_printf(file, "%3d -%3d | ",
+ bound[i] + 1, bound[i + 1]);
+
+ seq_puts(file, "\nCount: ");
+ for (i = 0; i < ARRAY_SIZE(bound); i++)
+ seq_printf(file, "%8d | ", phy->mt76->aggr_stats[i]);
+ seq_puts(file, "\n");
+
+ seq_printf(file, "BA miss count: %d\n", phy->mib.ba_miss_cnt);
+}
+
+static void
+mt7996_txbf_stat_read_phy(struct mt7996_phy *phy, struct seq_file *s)
+{
+ static const char * const bw[] = {
+ "BW20", "BW40", "BW80", "BW160"
+ };
+ struct mib_stats *mib = &phy->mib;
+
+ /* Tx Beamformer monitor */
+ seq_puts(s, "\nTx Beamformer applied PPDU counts: ");
+
+ seq_printf(s, "iBF: %d, eBF: %d\n",
+ mib->tx_bf_ibf_ppdu_cnt,
+ mib->tx_bf_ebf_ppdu_cnt);
+
+ /* Tx Beamformer Rx feedback monitor */
+ seq_puts(s, "Tx Beamformer Rx feedback statistics: ");
+
+ seq_printf(s, "All: %d, HE: %d, VHT: %d, HT: %d, ",
+ mib->tx_bf_rx_fb_all_cnt,
+ mib->tx_bf_rx_fb_he_cnt,
+ mib->tx_bf_rx_fb_vht_cnt,
+ mib->tx_bf_rx_fb_ht_cnt);
+
+ seq_printf(s, "%s, NC: %d, NR: %d\n",
+ bw[mib->tx_bf_rx_fb_bw],
+ mib->tx_bf_rx_fb_nc_cnt,
+ mib->tx_bf_rx_fb_nr_cnt);
+
+ /* Tx Beamformee Rx NDPA & Tx feedback report */
+ seq_printf(s, "Tx Beamformee successful feedback frames: %d\n",
+ mib->tx_bf_fb_cpl_cnt);
+ seq_printf(s, "Tx Beamformee feedback triggered counts: %d\n",
+ mib->tx_bf_fb_trig_cnt);
+
+ /* Tx SU & MU counters */
+ seq_printf(s, "Tx multi-user Beamforming counts: %d\n",
+ mib->tx_mu_bf_cnt);
+ seq_printf(s, "Tx multi-user MPDU counts: %d\n", mib->tx_mu_mpdu_cnt);
+ seq_printf(s, "Tx multi-user successful MPDU counts: %d\n",
+ mib->tx_mu_acked_mpdu_cnt);
+ seq_printf(s, "Tx single-user successful MPDU counts: %d\n",
+ mib->tx_su_acked_mpdu_cnt);
+
+ seq_puts(s, "\n");
+}
+
+static int
+mt7996_tx_stats_show(struct seq_file *file, void *data)
+{
+ struct mt7996_phy *phy = file->private;
+ struct mt7996_dev *dev = phy->dev;
+ struct mib_stats *mib = &phy->mib;
+ int i;
+ u32 attempts, success, per;
+
+ mutex_lock(&dev->mt76.mutex);
+
+ mt7996_mac_update_stats(phy);
+ mt7996_ampdu_stat_read_phy(phy, file);
+
+ attempts = mib->tx_mpdu_attempts_cnt;
+ success = mib->tx_mpdu_success_cnt;
+ per = attempts ? 100 - success * 100 / attempts : 100;
+ seq_printf(file, "Tx attempts: %8u (MPDUs)\n", attempts);
+ seq_printf(file, "Tx success: %8u (MPDUs)\n", success);
+ seq_printf(file, "Tx PER: %u%%\n", per);
+
+ mt7996_txbf_stat_read_phy(phy, file);
+
+ /* Tx amsdu info */
+ seq_puts(file, "Tx MSDU statistics:\n");
+ for (i = 0; i < ARRAY_SIZE(mib->tx_amsdu); i++) {
+ seq_printf(file, "AMSDU pack count of %d MSDU in TXD: %8d ",
+ i + 1, mib->tx_amsdu[i]);
+ if (mib->tx_amsdu_cnt)
+ seq_printf(file, "(%3d%%)\n",
+ mib->tx_amsdu[i] * 100 / mib->tx_amsdu_cnt);
+ else
+ seq_puts(file, "\n");
+ }
+
+ mutex_unlock(&dev->mt76.mutex);
+
+ return 0;
+}
+
+DEFINE_SHOW_ATTRIBUTE(mt7996_tx_stats);
+
+static void
+mt7996_hw_queue_read(struct seq_file *s, u32 size,
+ const struct hw_queue_map *map)
+{
+ struct mt7996_phy *phy = s->private;
+ struct mt7996_dev *dev = phy->dev;
+ u32 i, val;
+
+ val = mt76_rr(dev, MT_FL_Q_EMPTY);
+ for (i = 0; i < size; i++) {
+ u32 ctrl, head, tail, queued;
+
+ if (val & BIT(map[i].index))
+ continue;
+
+ ctrl = BIT(31) | (map[i].pid << 10) | (map[i].qid << 24);
+ mt76_wr(dev, MT_FL_Q0_CTRL, ctrl);
+
+ head = mt76_get_field(dev, MT_FL_Q2_CTRL,
+ GENMASK(11, 0));
+ tail = mt76_get_field(dev, MT_FL_Q2_CTRL,
+ GENMASK(27, 16));
+ queued = mt76_get_field(dev, MT_FL_Q3_CTRL,
+ GENMASK(11, 0));
+
+ seq_printf(s, "\t%s: ", map[i].name);
+ seq_printf(s, "queued:0x%03x head:0x%03x tail:0x%03x\n",
+ queued, head, tail);
+ }
+}
+
+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->phy->dev;
+ struct seq_file *s = data;
+ u8 ac;
+
+ for (ac = 0; ac < 4; ac++) {
+ u32 qlen, ctrl, val;
+ u32 idx = msta->wcid.idx >> 5;
+ u8 offs = msta->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->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->mt76.wmm_idx, ac, qlen);
+ }
+}
+
+static int
+mt7996_hw_queues_show(struct seq_file *file, void *data)
+{
+ struct mt7996_phy *phy = file->private;
+ struct mt7996_dev *dev = phy->dev;
+ static const struct hw_queue_map ple_queue_map[] = {
+ { "CPU_Q0", 0, 1, MT_CTX0 },
+ { "CPU_Q1", 1, 1, MT_CTX0 + 1 },
+ { "CPU_Q2", 2, 1, MT_CTX0 + 2 },
+ { "CPU_Q3", 3, 1, MT_CTX0 + 3 },
+ { "ALTX_Q0", 8, 2, MT_LMAC_ALTX0 },
+ { "BMC_Q0", 9, 2, MT_LMAC_BMC0 },
+ { "BCN_Q0", 10, 2, MT_LMAC_BCN0 },
+ { "PSMP_Q0", 11, 2, MT_LMAC_PSMP0 },
+ { "ALTX_Q1", 12, 2, MT_LMAC_ALTX0 + 4 },
+ { "BMC_Q1", 13, 2, MT_LMAC_BMC0 + 4 },
+ { "BCN_Q1", 14, 2, MT_LMAC_BCN0 + 4 },
+ { "PSMP_Q1", 15, 2, MT_LMAC_PSMP0 + 4 },
+ };
+ static const struct hw_queue_map pse_queue_map[] = {
+ { "CPU Q0", 0, 1, MT_CTX0 },
+ { "CPU Q1", 1, 1, MT_CTX0 + 1 },
+ { "CPU Q2", 2, 1, MT_CTX0 + 2 },
+ { "CPU Q3", 3, 1, MT_CTX0 + 3 },
+ { "HIF_Q0", 8, 0, MT_HIF0 },
+ { "HIF_Q1", 9, 0, MT_HIF0 + 1 },
+ { "HIF_Q2", 10, 0, MT_HIF0 + 2 },
+ { "HIF_Q3", 11, 0, MT_HIF0 + 3 },
+ { "HIF_Q4", 12, 0, MT_HIF0 + 4 },
+ { "HIF_Q5", 13, 0, MT_HIF0 + 5 },
+ { "LMAC_Q", 16, 2, 0 },
+ { "MDP_TXQ", 17, 2, 1 },
+ { "MDP_RXQ", 18, 2, 2 },
+ { "SEC_TXQ", 19, 2, 3 },
+ { "SEC_RXQ", 20, 2, 4 },
+ };
+ u32 val, head, tail;
+
+ /* ple queue */
+ val = mt76_rr(dev, MT_PLE_FREEPG_CNT);
+ head = mt76_get_field(dev, MT_PLE_FREEPG_HEAD_TAIL, GENMASK(11, 0));
+ tail = mt76_get_field(dev, MT_PLE_FREEPG_HEAD_TAIL, GENMASK(27, 16));
+ seq_puts(file, "PLE page info:\n");
+ seq_printf(file,
+ "\tTotal free page: 0x%08x head: 0x%03x tail: 0x%03x\n",
+ val, head, tail);
+
+ val = mt76_rr(dev, MT_PLE_PG_HIF_GROUP);
+ head = mt76_get_field(dev, MT_PLE_HIF_PG_INFO, GENMASK(11, 0));
+ tail = mt76_get_field(dev, MT_PLE_HIF_PG_INFO, GENMASK(27, 16));
+ seq_printf(file, "\tHIF free page: 0x%03x res: 0x%03x used: 0x%03x\n",
+ val, head, tail);
+
+ seq_puts(file, "PLE non-empty queue info:\n");
+ mt7996_hw_queue_read(file, ARRAY_SIZE(ple_queue_map),
+ &ple_queue_map[0]);
+
+ /* iterate per-sta ple queue */
+ ieee80211_iterate_stations_atomic(phy->mt76->hw,
+ mt7996_sta_hw_queue_read, file);
+ /* pse queue */
+ seq_puts(file, "PSE non-empty queue info:\n");
+ mt7996_hw_queue_read(file, ARRAY_SIZE(pse_queue_map),
+ &pse_queue_map[0]);
+
+ return 0;
+}
+
+DEFINE_SHOW_ATTRIBUTE(mt7996_hw_queues);
+
+static int
+mt7996_xmit_queues_show(struct seq_file *file, void *data)
+{
+ struct mt7996_phy *phy = file->private;
+ struct mt7996_dev *dev = phy->dev;
+ struct {
+ struct mt76_queue *q;
+ char *queue;
+ } queue_map[] = {
+ { phy->mt76->q_tx[MT_TXQ_BE], " MAIN" },
+ { dev->mt76.q_mcu[MT_MCUQ_WM], " MCUWM" },
+ { dev->mt76.q_mcu[MT_MCUQ_WA], " MCUWA" },
+ { dev->mt76.q_mcu[MT_MCUQ_FWDL], "MCUFWDL" },
+ };
+ int i;
+
+ seq_puts(file, " queue | hw-queued | head | tail |\n");
+ for (i = 0; i < ARRAY_SIZE(queue_map); i++) {
+ struct mt76_queue *q = queue_map[i].q;
+
+ if (!q)
+ continue;
+
+ seq_printf(file, " %s | %9d | %9d | %9d |\n",
+ queue_map[i].queue, q->queued, q->head,
+ q->tail);
+ }
+
+ return 0;
+}
+
+DEFINE_SHOW_ATTRIBUTE(mt7996_xmit_queues);
+
+static int
+mt7996_twt_stats(struct seq_file *s, void *data)
+{
+ struct mt7996_dev *dev = dev_get_drvdata(s->private);
+ struct mt7996_twt_flow *iter;
+
+ rcu_read_lock();
+
+ seq_puts(s, " wcid | id | flags | exp | mantissa");
+ seq_puts(s, " | duration | tsf |\n");
+ list_for_each_entry_rcu(iter, &dev->twt_list, list)
+ seq_printf(s,
+ "%9d | %8d | %5c%c%c%c | %8d | %8d | %8d | %14lld |\n",
+ iter->wcid, iter->id,
+ iter->sched ? 's' : 'u',
+ iter->protection ? 'p' : '-',
+ iter->trigger ? 't' : '-',
+ iter->flowtype ? '-' : 'a',
+ iter->exp, iter->mantissa,
+ iter->duration, iter->tsf);
+
+ rcu_read_unlock();
+
+ return 0;
+}
+
+/* The index of RF registers use the generic regidx, combined with two parts:
+ * WF selection [31:24] and offset [23:0].
+ */
+static int
+mt7996_rf_regval_get(void *data, u64 *val)
+{
+ struct mt7996_dev *dev = data;
+ u32 regval;
+ int ret;
+
+ ret = mt7996_mcu_rf_regval(dev, dev->mt76.debugfs_reg, &regval, false);
+ if (ret)
+ return ret;
+
+ *val = regval;
+
+ return 0;
+}
+
+static int
+mt7996_rf_regval_set(void *data, u64 val)
+{
+ struct mt7996_dev *dev = data;
+
+ return mt7996_mcu_rf_regval(dev, dev->mt76.debugfs_reg, (u32 *)&val, true);
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(fops_rf_regval, mt7996_rf_regval_get,
+ mt7996_rf_regval_set, "0x%08llx\n");
+
+int mt7996_init_debugfs(struct mt7996_phy *phy)
+{
+ struct mt7996_dev *dev = phy->dev;
+ struct dentry *dir;
+
+ dir = mt76_register_debugfs_fops(phy->mt76, NULL);
+ if (!dir)
+ return -ENOMEM;
+ debugfs_create_file("hw-queues", 0400, dir, phy,
+ &mt7996_hw_queues_fops);
+ debugfs_create_file("xmit-queues", 0400, dir, phy,
+ &mt7996_xmit_queues_fops);
+ debugfs_create_file("tx_stats", 0400, dir, phy, &mt7996_tx_stats_fops);
+ debugfs_create_file("fw_debug_wm", 0600, dir, dev, &fops_fw_debug_wm);
+ debugfs_create_file("fw_debug_wa", 0600, dir, dev, &fops_fw_debug_wa);
+ debugfs_create_file("fw_debug_bin", 0600, dir, dev, &fops_fw_debug_bin);
+ /* TODO: wm fw cpu utilization */
+ debugfs_create_file("fw_util_wa", 0400, dir, dev,
+ &mt7996_fw_util_wa_fops);
+ debugfs_create_file("implicit_txbf", 0600, dir, dev,
+ &fops_implicit_txbf);
+ debugfs_create_devm_seqfile(dev->mt76.dev, "twt_stats", dir,
+ mt7996_twt_stats);
+ debugfs_create_file("fw_ser", 0600, dir, phy, &mt7996_fw_ser_ops);
+ debugfs_create_file("rf_regval", 0600, dir, dev, &fops_rf_regval);
+
+ if (phy->mt76->cap.has_5ghz) {
+ debugfs_create_u32("dfs_hw_pattern", 0400, dir,
+ &dev->hw_pattern);
+ debugfs_create_file("radar_trigger", 0200, dir, dev,
+ &fops_radar_trigger);
+ debugfs_create_devm_seqfile(dev->mt76.dev, "rdd_monitor", dir,
+ mt7996_rdd_monitor);
+ }
+
+ if (phy == &dev->phy)
+ dev->debugfs_dir = dir;
+
+ return 0;
+}
+
+static void
+mt7996_debugfs_write_fwlog(struct mt7996_dev *dev, const void *hdr, int hdrlen,
+ const void *data, int len)
+{
+ static DEFINE_SPINLOCK(lock);
+ unsigned long flags;
+ void *dest;
+
+ spin_lock_irqsave(&lock, flags);
+ dest = relay_reserve(dev->relay_fwlog, hdrlen + len + 4);
+ if (dest) {
+ *(u32 *)dest = hdrlen + len;
+ dest += 4;
+
+ if (hdrlen) {
+ memcpy(dest, hdr, hdrlen);
+ dest += hdrlen;
+ }
+
+ memcpy(dest, data, len);
+ relay_flush(dev->relay_fwlog);
+ }
+ spin_unlock_irqrestore(&lock, flags);
+}
+
+void mt7996_debugfs_rx_fw_monitor(struct mt7996_dev *dev, const void *data, int len)
+{
+ struct {
+ __le32 magic;
+ u8 version;
+ u8 _rsv;
+ __le16 serial_id;
+ __le32 timestamp;
+ __le16 msg_type;
+ __le16 len;
+ } hdr = {
+ .version = 0x1,
+ .magic = cpu_to_le32(FW_BIN_LOG_MAGIC),
+ .msg_type = cpu_to_le16(PKT_TYPE_RX_FW_MONITOR),
+ };
+
+ if (!dev->relay_fwlog)
+ return;
+
+ hdr.serial_id = cpu_to_le16(dev->fw_debug_seq++);
+ hdr.timestamp = cpu_to_le32(mt76_rr(dev, MT_LPON_FRCR(0)));
+ hdr.len = *(__le16 *)data;
+ mt7996_debugfs_write_fwlog(dev, &hdr, sizeof(hdr), data, len);
+}
+
+bool mt7996_debugfs_rx_log(struct mt7996_dev *dev, const void *data, int len)
+{
+ if (get_unaligned_le32(data) != FW_BIN_LOG_MAGIC)
+ return false;
+
+ if (dev->relay_fwlog)
+ mt7996_debugfs_write_fwlog(dev, NULL, 0, data, len);
+
+ return true;
+}
+
+#ifdef CONFIG_MAC80211_DEBUGFS
+/** per-station debugfs **/
+
+static ssize_t mt7996_sta_fixed_rate_set(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+#define SHORT_PREAMBLE 0
+#define LONG_PREAMBLE 1
+ struct ieee80211_sta *sta = file->private_data;
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ struct mt7996_dev *dev = msta->vif->phy->dev;
+ struct ra_rate phy = {};
+ char buf[100];
+ int ret;
+ u16 gi, ltf;
+
+ if (count >= sizeof(buf))
+ return -EINVAL;
+
+ if (copy_from_user(buf, user_buf, count))
+ return -EFAULT;
+
+ if (count && buf[count - 1] == '\n')
+ buf[count - 1] = '\0';
+ else
+ buf[count] = '\0';
+
+ /* mode - cck: 0, ofdm: 1, ht: 2, gf: 3, vht: 4, he_su: 8, he_er: 9
+ * bw - bw20: 0, bw40: 1, bw80: 2, bw160: 3
+ * nss - vht: 1~4, he: 1~4, others: ignore
+ * mcs - cck: 0~4, ofdm: 0~7, ht: 0~32, vht: 0~9, he_su: 0~11, he_er: 0~2
+ * gi - (ht/vht) lgi: 0, sgi: 1; (he) 0.8us: 0, 1.6us: 1, 3.2us: 2
+ * preamble - short: 1, long: 0
+ * ldpc - off: 0, on: 1
+ * stbc - off: 0, on: 1
+ * ltf - 1xltf: 0, 2xltf: 1, 4xltf: 2
+ */
+ if (sscanf(buf, "%hhu %hhu %hhu %hhu %hu %hhu %hhu %hhu %hhu %hu",
+ &phy.mode, &phy.bw, &phy.mcs, &phy.nss, &gi,
+ &phy.preamble, &phy.stbc, &phy.ldpc, &phy.spe, &ltf) != 10) {
+ dev_warn(dev->mt76.dev,
+ "format: Mode BW MCS NSS GI Preamble STBC LDPC SPE ltf\n");
+ goto out;
+ }
+
+ phy.wlan_idx = cpu_to_le16(msta->wcid.idx);
+ phy.gi = cpu_to_le16(gi);
+ phy.ltf = cpu_to_le16(ltf);
+ phy.ldpc = phy.ldpc ? 7 : 0;
+ phy.preamble = phy.preamble ? SHORT_PREAMBLE : LONG_PREAMBLE;
+
+ ret = mt7996_mcu_set_fixed_rate_ctrl(dev, &phy, 0);
+ if (ret)
+ return -EFAULT;
+
+out:
+ return count;
+}
+
+static const struct file_operations fops_fixed_rate = {
+ .write = mt7996_sta_fixed_rate_set,
+ .open = simple_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+static int
+mt7996_queues_show(struct seq_file *s, void *data)
+{
+ struct ieee80211_sta *sta = s->private;
+
+ mt7996_sta_hw_queue_read(s, sta);
+
+ return 0;
+}
+
+DEFINE_SHOW_ATTRIBUTE(mt7996_queues);
+
+void mt7996_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, struct dentry *dir)
+{
+ debugfs_create_file("fixed_rate", 0600, dir, sta, &fops_fixed_rate);
+ debugfs_create_file("hw-queues", 0400, dir, sta, &mt7996_queues_fops);
+}
+
+#endif
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/dma.c b/drivers/net/wireless/mediatek/mt76/mt7996/dma.c
new file mode 100644
index 000000000000..c09fe4274935
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/dma.c
@@ -0,0 +1,360 @@
+// SPDX-License-Identifier: ISC
+/*
+ * Copyright (C) 2022 MediaTek Inc.
+ */
+
+#include "mt7996.h"
+#include "../dma.h"
+#include "mac.h"
+
+static int mt7996_poll_tx(struct napi_struct *napi, int budget)
+{
+ struct mt7996_dev *dev;
+
+ dev = container_of(napi, struct mt7996_dev, mt76.tx_napi);
+
+ mt76_connac_tx_cleanup(&dev->mt76);
+ if (napi_complete_done(napi, 0))
+ mt7996_irq_enable(dev, MT_INT_TX_DONE_MCU);
+
+ return 0;
+}
+
+static void mt7996_dma_config(struct mt7996_dev *dev)
+{
+#define Q_CONFIG(q, wfdma, int, id) do { \
+ if (wfdma) \
+ dev->q_wfdma_mask |= (1 << (q)); \
+ dev->q_int_mask[(q)] = int; \
+ dev->q_id[(q)] = id; \
+} while (0)
+
+#define MCUQ_CONFIG(q, wfdma, int, id) Q_CONFIG(q, (wfdma), (int), (id))
+#define RXQ_CONFIG(q, wfdma, int, id) Q_CONFIG(__RXQ(q), (wfdma), (int), (id))
+#define TXQ_CONFIG(q, wfdma, int, id) Q_CONFIG(__TXQ(q), (wfdma), (int), (id))
+
+ /* rx queue */
+ RXQ_CONFIG(MT_RXQ_MCU, WFDMA0, MT_INT_RX_DONE_WM, MT7996_RXQ_MCU_WM);
+ RXQ_CONFIG(MT_RXQ_MCU_WA, WFDMA0, MT_INT_RX_DONE_WA, MT7996_RXQ_MCU_WA);
+
+ /* band0/band1 */
+ 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);
+
+ /* 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);
+
+ /* data tx queue */
+ TXQ_CONFIG(0, WFDMA0, MT_INT_TX_DONE_BAND0, MT7996_TXQ_BAND0);
+ TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND1, MT7996_TXQ_BAND1);
+ TXQ_CONFIG(2, WFDMA0, MT_INT_TX_DONE_BAND2, MT7996_TXQ_BAND2);
+
+ /* 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);
+}
+
+static void __mt7996_dma_prefetch(struct mt7996_dev *dev, u32 ofs)
+{
+#define PREFETCH(_base, _depth) ((_base) << 16 | (_depth))
+ /* prefetch SRAM wrapping boundary for tx/rx ring. */
+ mt76_wr(dev, MT_MCUQ_EXT_CTRL(MT_MCUQ_FWDL) + ofs, PREFETCH(0x0, 0x2));
+ mt76_wr(dev, MT_MCUQ_EXT_CTRL(MT_MCUQ_WM) + ofs, PREFETCH(0x20, 0x2));
+ mt76_wr(dev, MT_TXQ_EXT_CTRL(0) + ofs, PREFETCH(0x40, 0x4));
+ mt76_wr(dev, MT_TXQ_EXT_CTRL(1) + ofs, PREFETCH(0x80, 0x4));
+ mt76_wr(dev, MT_MCUQ_EXT_CTRL(MT_MCUQ_WA) + ofs, PREFETCH(0xc0, 0x2));
+ mt76_wr(dev, MT_TXQ_EXT_CTRL(2) + ofs, PREFETCH(0xe0, 0x4));
+ mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_MCU) + ofs, PREFETCH(0x120, 0x2));
+ mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_MCU_WA) + ofs, PREFETCH(0x140, 0x2));
+ mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_MAIN_WA) + ofs, PREFETCH(0x160, 0x2));
+ mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_BAND2_WA) + ofs, PREFETCH(0x180, 0x2));
+ mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_MAIN) + ofs, PREFETCH(0x1a0, 0x10));
+ mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_BAND2) + ofs, PREFETCH(0x2a0, 0x10));
+
+ mt76_set(dev, WF_WFDMA0_GLO_CFG_EXT1 + ofs, WF_WFDMA0_GLO_CFG_EXT1_CALC_MODE);
+}
+
+void mt7996_dma_prefetch(struct mt7996_dev *dev)
+{
+ __mt7996_dma_prefetch(dev, 0);
+ if (dev->hif2)
+ __mt7996_dma_prefetch(dev, MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0));
+}
+
+static void mt7996_dma_disable(struct mt7996_dev *dev, bool reset)
+{
+ u32 hif1_ofs = 0;
+
+ if (dev->hif2)
+ hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0);
+
+ if (reset) {
+ mt76_clear(dev, MT_WFDMA0_RST,
+ MT_WFDMA0_RST_DMASHDL_ALL_RST |
+ MT_WFDMA0_RST_LOGIC_RST);
+
+ mt76_set(dev, MT_WFDMA0_RST,
+ MT_WFDMA0_RST_DMASHDL_ALL_RST |
+ MT_WFDMA0_RST_LOGIC_RST);
+
+ if (dev->hif2) {
+ mt76_clear(dev, MT_WFDMA0_RST + hif1_ofs,
+ MT_WFDMA0_RST_DMASHDL_ALL_RST |
+ MT_WFDMA0_RST_LOGIC_RST);
+
+ mt76_set(dev, MT_WFDMA0_RST + hif1_ofs,
+ MT_WFDMA0_RST_DMASHDL_ALL_RST |
+ MT_WFDMA0_RST_LOGIC_RST);
+ }
+ }
+
+ /* disable */
+ mt76_clear(dev, MT_WFDMA0_GLO_CFG,
+ MT_WFDMA0_GLO_CFG_TX_DMA_EN |
+ MT_WFDMA0_GLO_CFG_RX_DMA_EN |
+ MT_WFDMA0_GLO_CFG_OMIT_TX_INFO |
+ MT_WFDMA0_GLO_CFG_OMIT_RX_INFO |
+ MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2);
+
+ if (dev->hif2) {
+ mt76_clear(dev, MT_WFDMA0_GLO_CFG + hif1_ofs,
+ MT_WFDMA0_GLO_CFG_TX_DMA_EN |
+ MT_WFDMA0_GLO_CFG_RX_DMA_EN |
+ MT_WFDMA0_GLO_CFG_OMIT_TX_INFO |
+ MT_WFDMA0_GLO_CFG_OMIT_RX_INFO |
+ MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2);
+ }
+}
+
+static int mt7996_dma_enable(struct mt7996_dev *dev)
+{
+ u32 hif1_ofs = 0;
+ u32 irq_mask;
+
+ if (dev->hif2)
+ hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0);
+
+ /* reset dma idx */
+ mt76_wr(dev, MT_WFDMA0_RST_DTX_PTR, ~0);
+ if (dev->hif2)
+ mt76_wr(dev, MT_WFDMA0_RST_DTX_PTR + hif1_ofs, ~0);
+
+ /* configure delay interrupt off */
+ mt76_wr(dev, MT_WFDMA0_PRI_DLY_INT_CFG0, 0);
+ mt76_wr(dev, MT_WFDMA0_PRI_DLY_INT_CFG1, 0);
+ mt76_wr(dev, MT_WFDMA0_PRI_DLY_INT_CFG2, 0);
+
+ if (dev->hif2) {
+ mt76_wr(dev, MT_WFDMA0_PRI_DLY_INT_CFG0 + hif1_ofs, 0);
+ mt76_wr(dev, MT_WFDMA0_PRI_DLY_INT_CFG1 + hif1_ofs, 0);
+ mt76_wr(dev, MT_WFDMA0_PRI_DLY_INT_CFG2 + hif1_ofs, 0);
+ }
+
+ /* configure perfetch settings */
+ mt7996_dma_prefetch(dev);
+
+ /* hif wait WFDMA idle */
+ mt76_set(dev, MT_WFDMA0_BUSY_ENA,
+ MT_WFDMA0_BUSY_ENA_TX_FIFO0 |
+ MT_WFDMA0_BUSY_ENA_TX_FIFO1 |
+ MT_WFDMA0_BUSY_ENA_RX_FIFO);
+
+ if (dev->hif2)
+ mt76_set(dev, MT_WFDMA0_BUSY_ENA + hif1_ofs,
+ MT_WFDMA0_PCIE1_BUSY_ENA_TX_FIFO0 |
+ MT_WFDMA0_PCIE1_BUSY_ENA_TX_FIFO1 |
+ MT_WFDMA0_PCIE1_BUSY_ENA_RX_FIFO);
+
+ mt76_poll(dev, MT_WFDMA_EXT_CSR_HIF_MISC,
+ MT_WFDMA_EXT_CSR_HIF_MISC_BUSY, 0, 1000);
+
+ /* set WFDMA Tx/Rx */
+ mt76_set(dev, MT_WFDMA0_GLO_CFG,
+ MT_WFDMA0_GLO_CFG_TX_DMA_EN |
+ MT_WFDMA0_GLO_CFG_RX_DMA_EN |
+ MT_WFDMA0_GLO_CFG_OMIT_TX_INFO |
+ MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2);
+
+ /* GLO_CFG_EXT0 */
+ mt76_set(dev, WF_WFDMA0_GLO_CFG_EXT0,
+ WF_WFDMA0_GLO_CFG_EXT0_RX_WB_RXD |
+ WF_WFDMA0_GLO_CFG_EXT0_WED_MERGE_MODE);
+
+ /* GLO_CFG_EXT1 */
+ mt76_set(dev, WF_WFDMA0_GLO_CFG_EXT1,
+ WF_WFDMA0_GLO_CFG_EXT1_TX_FCTRL_MODE);
+
+ if (dev->hif2) {
+ mt76_set(dev, MT_WFDMA0_GLO_CFG + hif1_ofs,
+ MT_WFDMA0_GLO_CFG_TX_DMA_EN |
+ MT_WFDMA0_GLO_CFG_RX_DMA_EN |
+ MT_WFDMA0_GLO_CFG_OMIT_TX_INFO |
+ MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2);
+
+ /* GLO_CFG_EXT0 */
+ mt76_set(dev, WF_WFDMA0_GLO_CFG_EXT0 + hif1_ofs,
+ WF_WFDMA0_GLO_CFG_EXT0_RX_WB_RXD |
+ WF_WFDMA0_GLO_CFG_EXT0_WED_MERGE_MODE);
+
+ /* GLO_CFG_EXT1 */
+ mt76_set(dev, WF_WFDMA0_GLO_CFG_EXT1 + hif1_ofs,
+ WF_WFDMA0_GLO_CFG_EXT1_TX_FCTRL_MODE);
+
+ mt76_set(dev, MT_WFDMA_HOST_CONFIG,
+ MT_WFDMA_HOST_CONFIG_PDMA_BAND);
+ }
+
+ if (dev->hif2) {
+ /* fix hardware limitation, pcie1's rx ring3 is not available
+ * so, redirect pcie0 rx ring3 interrupt to pcie1
+ */
+ mt76_set(dev, MT_WFDMA0_RX_INT_PCIE_SEL,
+ MT_WFDMA0_RX_INT_SEL_RING3);
+
+ /* TODO: redirect rx ring6 interrupt to pcie0 for wed function */
+ }
+
+ /* enable interrupts for TX/RX rings */
+ irq_mask = MT_INT_RX_DONE_MCU |
+ MT_INT_TX_DONE_MCU |
+ MT_INT_MCU_CMD;
+
+ if (!dev->mphy.band_idx)
+ irq_mask |= MT_INT_BAND0_RX_DONE;
+
+ if (dev->dbdc_support)
+ irq_mask |= MT_INT_BAND1_RX_DONE;
+
+ if (dev->tbtc_support)
+ irq_mask |= MT_INT_BAND2_RX_DONE;
+
+ mt7996_irq_enable(dev, irq_mask);
+
+ return 0;
+}
+
+int mt7996_dma_init(struct mt7996_dev *dev)
+{
+ u32 hif1_ofs = 0;
+ int ret;
+
+ mt7996_dma_config(dev);
+
+ mt76_dma_attach(&dev->mt76);
+
+ if (dev->hif2)
+ hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0);
+
+ mt7996_dma_disable(dev, true);
+
+ /* init tx queue */
+ ret = mt76_connac_init_tx_queues(dev->phy.mt76,
+ MT_TXQ_ID(dev->mphy.band_idx),
+ MT7996_TX_RING_SIZE,
+ MT_TXQ_RING_BASE(0), 0);
+ if (ret)
+ return ret;
+
+ /* command to WM */
+ ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_WM,
+ MT_MCUQ_ID(MT_MCUQ_WM),
+ MT7996_TX_MCU_RING_SIZE,
+ MT_MCUQ_RING_BASE(MT_MCUQ_WM));
+ if (ret)
+ 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;
+
+ /* firmware download */
+ ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_FWDL,
+ MT_MCUQ_ID(MT_MCUQ_FWDL),
+ MT7996_TX_FWDL_RING_SIZE,
+ MT_MCUQ_RING_BASE(MT_MCUQ_FWDL));
+ if (ret)
+ return ret;
+
+ /* event from WM */
+ 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,
+ MT_RXQ_RING_BASE(MT_RXQ_MCU));
+ if (ret)
+ return ret;
+
+ /* event from WA */
+ 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,
+ MT_RX_BUF_SIZE,
+ MT_RXQ_RING_BASE(MT_RXQ_MCU_WA));
+ if (ret)
+ return ret;
+
+ /* rx data queue for band0 and band1 */
+ ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MAIN],
+ MT_RXQ_ID(MT_RXQ_MAIN),
+ MT7996_RX_RING_SIZE,
+ MT_RX_BUF_SIZE,
+ MT_RXQ_RING_BASE(MT_RXQ_MAIN));
+ if (ret)
+ return ret;
+
+ /* tx free notify event from WA for band0 */
+ 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 (dev->tbtc_support || dev->mphy.band_idx == MT_BAND2) {
+ /* rx data queue for band2 */
+ ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_BAND2],
+ MT_RXQ_ID(MT_RXQ_BAND2),
+ MT7996_RX_RING_SIZE,
+ MT_RX_BUF_SIZE,
+ MT_RXQ_RING_BASE(MT_RXQ_BAND2) + hif1_ofs);
+ if (ret)
+ return ret;
+
+ /* tx free notify event from WA for band2
+ * use pcie0's rx ring3, but, redirect pcie0 rx ring3 interrupt to pcie1
+ */
+ ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_BAND2_WA],
+ MT_RXQ_ID(MT_RXQ_BAND2_WA),
+ MT7996_RX_MCU_RING_SIZE,
+ MT_RX_BUF_SIZE,
+ MT_RXQ_RING_BASE(MT_RXQ_BAND2_WA));
+ if (ret)
+ return ret;
+ }
+
+ ret = mt76_init_queues(dev, mt76_dma_rx_poll);
+ if (ret < 0)
+ return ret;
+
+ netif_napi_add_tx(&dev->mt76.tx_napi_dev, &dev->mt76.tx_napi,
+ mt7996_poll_tx);
+ napi_enable(&dev->mt76.tx_napi);
+
+ mt7996_dma_enable(dev);
+
+ return 0;
+}
+
+void mt7996_dma_cleanup(struct mt7996_dev *dev)
+{
+ mt7996_dma_disable(dev, true);
+
+ mt76_dma_cleanup(&dev->mt76);
+}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c
new file mode 100644
index 000000000000..b9f62bedbc48
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c
@@ -0,0 +1,229 @@
+// SPDX-License-Identifier: ISC
+/*
+ * Copyright (C) 2022 MediaTek Inc.
+ */
+
+#include <linux/firmware.h>
+#include "mt7996.h"
+#include "eeprom.h"
+
+static int mt7996_check_eeprom(struct mt7996_dev *dev)
+{
+ u8 *eeprom = dev->mt76.eeprom.data;
+ u16 val = get_unaligned_le16(eeprom);
+
+ switch (val) {
+ case 0x7990:
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static char *mt7996_eeprom_name(struct mt7996_dev *dev)
+{
+ /* reserve for future variants */
+ return MT7996_EEPROM_DEFAULT;
+}
+
+static int
+mt7996_eeprom_load_default(struct mt7996_dev *dev)
+{
+ u8 *eeprom = dev->mt76.eeprom.data;
+ const struct firmware *fw = NULL;
+ int ret;
+
+ ret = request_firmware(&fw, mt7996_eeprom_name(dev), dev->mt76.dev);
+ if (ret)
+ return ret;
+
+ if (!fw || !fw->data) {
+ dev_err(dev->mt76.dev, "Invalid default bin\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ memcpy(eeprom, fw->data, MT7996_EEPROM_SIZE);
+ dev->flash_mode = true;
+
+out:
+ release_firmware(fw);
+
+ return ret;
+}
+
+static int mt7996_eeprom_load(struct mt7996_dev *dev)
+{
+ int ret;
+
+ ret = mt76_eeprom_init(&dev->mt76, MT7996_EEPROM_SIZE);
+ if (ret < 0)
+ return ret;
+
+ if (ret) {
+ dev->flash_mode = true;
+ } else {
+ u8 free_block_num;
+ u32 block_num, i;
+
+ /* TODO: check free block event */
+ mt7996_mcu_get_eeprom_free_block(dev, &free_block_num);
+ /* efuse info not enough */
+ if (free_block_num >= 59)
+ return -EINVAL;
+
+ /* read eeprom data from efuse */
+ block_num = DIV_ROUND_UP(MT7996_EEPROM_SIZE, MT7996_EEPROM_BLOCK_SIZE);
+ for (i = 0; i < block_num; i++)
+ mt7996_mcu_get_eeprom(dev, i * MT7996_EEPROM_BLOCK_SIZE);
+ }
+
+ return mt7996_check_eeprom(dev);
+}
+
+static int mt7996_eeprom_parse_band_config(struct mt7996_phy *phy)
+{
+ u8 *eeprom = phy->dev->mt76.eeprom.data;
+ u32 val = eeprom[MT_EE_WIFI_CONF];
+ int ret = 0;
+
+ switch (phy->mt76->band_idx) {
+ case MT_BAND1:
+ val = FIELD_GET(MT_EE_WIFI_CONF1_BAND_SEL, val);
+ break;
+ case MT_BAND2:
+ val = eeprom[MT_EE_WIFI_CONF + 1];
+ val = FIELD_GET(MT_EE_WIFI_CONF2_BAND_SEL, val);
+ break;
+ default:
+ val = FIELD_GET(MT_EE_WIFI_CONF0_BAND_SEL, val);
+ break;
+ }
+
+ switch (val) {
+ case MT_EE_BAND_SEL_2GHZ:
+ phy->mt76->cap.has_2ghz = true;
+ break;
+ case MT_EE_BAND_SEL_5GHZ:
+ phy->mt76->cap.has_5ghz = true;
+ break;
+ case MT_EE_BAND_SEL_6GHZ:
+ phy->mt76->cap.has_6ghz = true;
+ break;
+ case MT_EE_BAND_SEL_5GHZ_6GHZ:
+ phy->mt76->cap.has_5ghz = true;
+ phy->mt76->cap.has_6ghz = true;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+int mt7996_eeprom_parse_hw_cap(struct mt7996_dev *dev, struct mt7996_phy *phy)
+{
+ u8 path, nss, band_idx = phy->mt76->band_idx;
+ u8 *eeprom = dev->mt76.eeprom.data;
+ struct mt76_phy *mphy = phy->mt76;
+
+ switch (band_idx) {
+ case MT_BAND1:
+ path = FIELD_GET(MT_EE_WIFI_CONF2_TX_PATH_BAND1,
+ eeprom[MT_EE_WIFI_CONF + 2]);
+ nss = FIELD_GET(MT_EE_WIFI_CONF5_STREAM_NUM_BAND1,
+ eeprom[MT_EE_WIFI_CONF + 5]);
+ break;
+ case MT_BAND2:
+ path = FIELD_GET(MT_EE_WIFI_CONF2_TX_PATH_BAND2,
+ eeprom[MT_EE_WIFI_CONF + 2]);
+ nss = FIELD_GET(MT_EE_WIFI_CONF5_STREAM_NUM_BAND2,
+ eeprom[MT_EE_WIFI_CONF + 5]);
+ break;
+ default:
+ path = FIELD_GET(MT_EE_WIFI_CONF1_TX_PATH_BAND0,
+ eeprom[MT_EE_WIFI_CONF + 1]);
+ nss = FIELD_GET(MT_EE_WIFI_CONF4_STREAM_NUM_BAND0,
+ eeprom[MT_EE_WIFI_CONF + 4]);
+ break;
+ }
+
+ if (!path || path > 4)
+ path = 4;
+
+ nss = min_t(u8, min_t(u8, 4, nss), path);
+
+ mphy->antenna_mask = BIT(nss) - 1;
+ mphy->chainmask = (BIT(path) - 1) << dev->chainshift[band_idx];
+ dev->chainmask |= mphy->chainmask;
+ if (band_idx < MT_BAND2)
+ dev->chainshift[band_idx + 1] = dev->chainshift[band_idx] +
+ hweight16(mphy->chainmask);
+
+ return mt7996_eeprom_parse_band_config(phy);
+}
+
+int mt7996_eeprom_init(struct mt7996_dev *dev)
+{
+ int ret;
+
+ ret = mt7996_eeprom_load(dev);
+ if (ret < 0) {
+ if (ret != -EINVAL)
+ return ret;
+
+ dev_warn(dev->mt76.dev, "eeprom load fail, use default bin\n");
+ ret = mt7996_eeprom_load_default(dev);
+ if (ret)
+ return ret;
+ }
+
+ ret = mt7996_eeprom_parse_hw_cap(dev, &dev->phy);
+ if (ret < 0)
+ return ret;
+
+ memcpy(dev->mphy.macaddr, dev->mt76.eeprom.data + MT_EE_MAC_ADDR, ETH_ALEN);
+ mt76_eeprom_override(&dev->mphy);
+
+ return 0;
+}
+
+int mt7996_eeprom_get_target_power(struct mt7996_dev *dev,
+ struct ieee80211_channel *chan)
+{
+ u8 *eeprom = dev->mt76.eeprom.data;
+ int target_power;
+
+ if (chan->band == NL80211_BAND_5GHZ)
+ target_power = eeprom[MT_EE_TX0_POWER_5G +
+ mt7996_get_channel_group_5g(chan->hw_value)];
+ else if (chan->band == NL80211_BAND_6GHZ)
+ target_power = eeprom[MT_EE_TX0_POWER_6G +
+ mt7996_get_channel_group_6g(chan->hw_value)];
+ else
+ target_power = eeprom[MT_EE_TX0_POWER_2G];
+
+ return target_power;
+}
+
+s8 mt7996_eeprom_get_power_delta(struct mt7996_dev *dev, int band)
+{
+ u8 *eeprom = dev->mt76.eeprom.data;
+ u32 val;
+ s8 delta;
+
+ if (band == NL80211_BAND_5GHZ)
+ val = eeprom[MT_EE_RATE_DELTA_5G];
+ else if (band == NL80211_BAND_6GHZ)
+ val = eeprom[MT_EE_RATE_DELTA_6G];
+ else
+ val = eeprom[MT_EE_RATE_DELTA_2G];
+
+ if (!(val & MT_EE_RATE_DELTA_EN))
+ return 0;
+
+ delta = FIELD_GET(MT_EE_RATE_DELTA_MASK, val);
+
+ return val & MT_EE_RATE_DELTA_SIGN ? delta : -delta;
+}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.h b/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.h
new file mode 100644
index 000000000000..8da599e0abea
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.h
@@ -0,0 +1,75 @@
+/* SPDX-License-Identifier: ISC */
+/*
+ * Copyright (C) 2022 MediaTek Inc.
+ */
+
+#ifndef __MT7996_EEPROM_H
+#define __MT7996_EEPROM_H
+
+#include "mt7996.h"
+
+enum mt7996_eeprom_field {
+ MT_EE_CHIP_ID = 0x000,
+ MT_EE_VERSION = 0x002,
+ MT_EE_MAC_ADDR = 0x004,
+ MT_EE_MAC_ADDR2 = 0x00a,
+ MT_EE_WIFI_CONF = 0x190,
+ MT_EE_MAC_ADDR3 = 0x2c0,
+ MT_EE_RATE_DELTA_2G = 0x1400,
+ MT_EE_RATE_DELTA_5G = 0x147d,
+ MT_EE_RATE_DELTA_6G = 0x154a,
+ MT_EE_TX0_POWER_2G = 0x1300,
+ MT_EE_TX0_POWER_5G = 0x1301,
+ MT_EE_TX0_POWER_6G = 0x1310,
+
+ __MT_EE_MAX = 0x1dff,
+};
+
+#define MT_EE_WIFI_CONF0_TX_PATH GENMASK(2, 0)
+#define MT_EE_WIFI_CONF0_BAND_SEL GENMASK(2, 0)
+#define MT_EE_WIFI_CONF1_BAND_SEL GENMASK(5, 3)
+#define MT_EE_WIFI_CONF2_BAND_SEL GENMASK(2, 0)
+
+#define MT_EE_WIFI_CONF1_TX_PATH_BAND0 GENMASK(5, 3)
+#define MT_EE_WIFI_CONF2_TX_PATH_BAND1 GENMASK(5, 3)
+#define MT_EE_WIFI_CONF2_TX_PATH_BAND2 GENMASK(2, 0)
+#define MT_EE_WIFI_CONF4_STREAM_NUM_BAND0 GENMASK(5, 3)
+#define MT_EE_WIFI_CONF5_STREAM_NUM_BAND1 GENMASK(5, 3)
+#define MT_EE_WIFI_CONF5_STREAM_NUM_BAND2 GENMASK(2, 0)
+
+#define MT_EE_RATE_DELTA_MASK GENMASK(5, 0)
+#define MT_EE_RATE_DELTA_SIGN BIT(6)
+#define MT_EE_RATE_DELTA_EN BIT(7)
+
+enum mt7996_eeprom_band {
+ MT_EE_BAND_SEL_DEFAULT,
+ MT_EE_BAND_SEL_2GHZ,
+ MT_EE_BAND_SEL_5GHZ,
+ MT_EE_BAND_SEL_6GHZ,
+ MT_EE_BAND_SEL_5GHZ_6GHZ,
+};
+
+static inline int
+mt7996_get_channel_group_5g(int channel)
+{
+ if (channel <= 64)
+ return 0;
+ if (channel <= 96)
+ return 1;
+ if (channel <= 128)
+ return 2;
+ if (channel <= 144)
+ return 3;
+ return 4;
+}
+
+static inline int
+mt7996_get_channel_group_6g(int channel)
+{
+ if (channel <= 29)
+ return 0;
+
+ return DIV_ROUND_UP(channel - 29, 32);
+}
+
+#endif
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/init.c b/drivers/net/wireless/mediatek/mt76/mt7996/init.c
new file mode 100644
index 000000000000..46b290526092
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/init.c
@@ -0,0 +1,823 @@
+// SPDX-License-Identifier: ISC
+/*
+ * Copyright (C) 2022 MediaTek Inc.
+ */
+
+#include <linux/etherdevice.h>
+#include <linux/thermal.h>
+#include "mt7996.h"
+#include "mac.h"
+#include "mcu.h"
+#include "eeprom.h"
+
+static const struct ieee80211_iface_limit if_limits[] = {
+ {
+ .max = 1,
+ .types = BIT(NL80211_IFTYPE_ADHOC)
+ }, {
+ .max = 16,
+ .types = BIT(NL80211_IFTYPE_AP)
+#ifdef CONFIG_MAC80211_MESH
+ | BIT(NL80211_IFTYPE_MESH_POINT)
+#endif
+ }, {
+ .max = MT7996_MAX_INTERFACES,
+ .types = BIT(NL80211_IFTYPE_STATION)
+ }
+};
+
+static const struct ieee80211_iface_combination if_comb[] = {
+ {
+ .limits = if_limits,
+ .n_limits = ARRAY_SIZE(if_limits),
+ .max_interfaces = MT7996_MAX_INTERFACES,
+ .num_different_channels = 1,
+ .beacon_int_infra_match = true,
+ .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_160) |
+ BIT(NL80211_CHAN_WIDTH_80P80),
+ }
+};
+
+static void mt7996_led_set_config(struct led_classdev *led_cdev,
+ u8 delay_on, u8 delay_off)
+{
+ struct mt7996_dev *dev;
+ struct mt76_dev *mt76;
+ u32 val;
+
+ mt76 = container_of(led_cdev, struct mt76_dev, led_cdev);
+ dev = container_of(mt76, struct mt7996_dev, mt76);
+
+ /* select TX blink mode, 2: only data frames */
+ mt76_rmw_field(dev, MT_TMAC_TCR0(0), MT_TMAC_TCR0_TX_BLINK, 2);
+
+ /* enable LED */
+ mt76_wr(dev, MT_LED_EN(0), 1);
+
+ /* set LED Tx blink on/off time */
+ val = FIELD_PREP(MT_LED_TX_BLINK_ON_MASK, delay_on) |
+ FIELD_PREP(MT_LED_TX_BLINK_OFF_MASK, delay_off);
+ mt76_wr(dev, MT_LED_TX_BLINK(0), val);
+
+ /* control LED */
+ val = MT_LED_CTRL_BLINK_MODE | MT_LED_CTRL_KICK;
+ if (dev->mt76.led_al)
+ val |= MT_LED_CTRL_POLARITY;
+
+ mt76_wr(dev, MT_LED_CTRL(0), val);
+ mt76_clear(dev, MT_LED_CTRL(0), MT_LED_CTRL_KICK);
+}
+
+static int mt7996_led_set_blink(struct led_classdev *led_cdev,
+ unsigned long *delay_on,
+ unsigned long *delay_off)
+{
+ u16 delta_on = 0, delta_off = 0;
+
+#define HW_TICK 10
+#define TO_HW_TICK(_t) (((_t) > HW_TICK) ? ((_t) / HW_TICK) : HW_TICK)
+
+ if (*delay_on)
+ delta_on = TO_HW_TICK(*delay_on);
+ if (*delay_off)
+ delta_off = TO_HW_TICK(*delay_off);
+
+ mt7996_led_set_config(led_cdev, delta_on, delta_off);
+
+ return 0;
+}
+
+static void mt7996_led_set_brightness(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ if (!brightness)
+ mt7996_led_set_config(led_cdev, 0, 0xff);
+ else
+ mt7996_led_set_config(led_cdev, 0xff, 0);
+}
+
+static void
+mt7996_init_txpower(struct mt7996_dev *dev,
+ struct ieee80211_supported_band *sband)
+{
+ int i, nss = hweight8(dev->mphy.antenna_mask);
+ int nss_delta = mt76_tx_power_nss_delta(nss);
+ int pwr_delta = mt7996_eeprom_get_power_delta(dev, sband->band);
+ struct mt76_power_limits limits;
+
+ for (i = 0; i < sband->n_channels; i++) {
+ struct ieee80211_channel *chan = &sband->channels[i];
+ int target_power = mt7996_eeprom_get_target_power(dev, chan);
+
+ target_power += pwr_delta;
+ target_power = mt76_get_rate_power_limits(&dev->mphy, chan,
+ &limits,
+ target_power);
+ target_power += nss_delta;
+ target_power = DIV_ROUND_UP(target_power, 2);
+ chan->max_power = min_t(int, chan->max_reg_power,
+ target_power);
+ chan->orig_mpwr = target_power;
+ }
+}
+
+static void
+mt7996_regd_notifier(struct wiphy *wiphy,
+ struct regulatory_request *request)
+{
+ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+
+ memcpy(dev->mt76.alpha2, request->alpha2, sizeof(dev->mt76.alpha2));
+ dev->mt76.region = request->dfs_region;
+
+ if (dev->mt76.region == NL80211_DFS_UNSET)
+ mt7996_mcu_rdd_background_enable(phy, NULL);
+
+ mt7996_init_txpower(dev, &phy->mt76->sband_2g.sband);
+ mt7996_init_txpower(dev, &phy->mt76->sband_5g.sband);
+ mt7996_init_txpower(dev, &phy->mt76->sband_6g.sband);
+
+ phy->mt76->dfs_state = MT_DFS_STATE_UNKNOWN;
+ mt7996_dfs_init_radar_detector(phy);
+}
+
+static void
+mt7996_init_wiphy(struct ieee80211_hw *hw)
+{
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ struct mt76_dev *mdev = &phy->dev->mt76;
+ struct wiphy *wiphy = hw->wiphy;
+
+ hw->queues = 4;
+ hw->max_rx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF_HE;
+ hw->max_tx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF_HE;
+ hw->netdev_features = NETIF_F_RXCSUM;
+
+ hw->radiotap_timestamp.units_pos =
+ IEEE80211_RADIOTAP_TIMESTAMP_UNIT_US;
+
+ phy->slottime = 9;
+
+ hw->sta_data_size = sizeof(struct mt7996_sta);
+ hw->vif_data_size = sizeof(struct mt7996_vif);
+
+ wiphy->iface_combinations = if_comb;
+ wiphy->n_iface_combinations = ARRAY_SIZE(if_comb);
+ wiphy->reg_notifier = mt7996_regd_notifier;
+ wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
+
+ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BSS_COLOR);
+ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_VHT_IBSS);
+ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_LEGACY);
+ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_HT);
+ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_VHT);
+ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_HE);
+ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_UNSOL_BCAST_PROBE_RESP);
+ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_FILS_DISCOVERY);
+ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT);
+
+ if (!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);
+
+ ieee80211_hw_set(hw, HAS_RATE_CONTROL);
+ ieee80211_hw_set(hw, SUPPORTS_TX_ENCAP_OFFLOAD);
+ ieee80211_hw_set(hw, SUPPORTS_RX_DECAP_OFFLOAD);
+ ieee80211_hw_set(hw, WANT_MONITOR_VIF);
+
+ hw->max_tx_fragments = 4;
+
+ if (phy->mt76->cap.has_2ghz)
+ phy->mt76->sband_2g.sband.ht_cap.cap |=
+ IEEE80211_HT_CAP_LDPC_CODING |
+ IEEE80211_HT_CAP_MAX_AMSDU;
+
+ if (phy->mt76->cap.has_5ghz) {
+ phy->mt76->sband_5g.sband.ht_cap.cap |=
+ IEEE80211_HT_CAP_LDPC_CODING |
+ IEEE80211_HT_CAP_MAX_AMSDU;
+
+ phy->mt76->sband_5g.sband.vht_cap.cap |=
+ IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 |
+ IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK |
+ IEEE80211_VHT_CAP_SHORT_GI_160 |
+ IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
+ }
+
+ mt76_set_stream_caps(phy->mt76, true);
+ mt7996_set_stream_vht_txbf_caps(phy);
+ mt7996_set_stream_he_caps(phy);
+
+ wiphy->available_antennas_rx = phy->mt76->antenna_mask;
+ wiphy->available_antennas_tx = phy->mt76->antenna_mask;
+}
+
+static void
+mt7996_mac_init_band(struct mt7996_dev *dev, u8 band)
+{
+ u32 mask, set;
+
+ /* clear estimated value of EIFS for Rx duration & OBSS time */
+ mt76_wr(dev, MT_WF_RMAC_RSVD0(band), MT_WF_RMAC_RSVD0_EIFS_CLR);
+
+ /* clear backoff time for Rx duration */
+ mt76_clear(dev, MT_WF_RMAC_MIB_AIRTIME1(band),
+ MT_WF_RMAC_MIB_NONQOSD_BACKOFF);
+ mt76_clear(dev, MT_WF_RMAC_MIB_AIRTIME3(band),
+ MT_WF_RMAC_MIB_QOS01_BACKOFF);
+ mt76_clear(dev, MT_WF_RMAC_MIB_AIRTIME4(band),
+ MT_WF_RMAC_MIB_QOS23_BACKOFF);
+
+ /* clear backoff time and set software compensation for OBSS time */
+ mask = MT_WF_RMAC_MIB_OBSS_BACKOFF | MT_WF_RMAC_MIB_ED_OFFSET;
+ set = FIELD_PREP(MT_WF_RMAC_MIB_OBSS_BACKOFF, 0) |
+ FIELD_PREP(MT_WF_RMAC_MIB_ED_OFFSET, 4);
+ mt76_rmw(dev, MT_WF_RMAC_MIB_AIRTIME0(band), mask, set);
+
+ /* filter out non-resp frames and get instanstaeous signal reporting */
+ mask = MT_WTBLOFF_RSCR_RCPI_MODE | MT_WTBLOFF_RSCR_RCPI_PARAM;
+ set = FIELD_PREP(MT_WTBLOFF_RSCR_RCPI_MODE, 0) |
+ FIELD_PREP(MT_WTBLOFF_RSCR_RCPI_PARAM, 0x3);
+ mt76_rmw(dev, MT_WTBLOFF_RSCR(band), mask, set);
+}
+
+static void mt7996_mac_init(struct mt7996_dev *dev)
+{
+#define HIF_TXD_V2_1 4
+ int i;
+
+ mt76_clear(dev, MT_MDP_DCR2, MT_MDP_DCR2_RX_TRANS_SHORT);
+
+ for (i = 0; i < MT7996_WTBL_SIZE; i++)
+ mt7996_mac_wtbl_update(dev, i,
+ MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
+
+ if (IS_ENABLED(CONFIG_MT76_LEDS)) {
+ i = dev->mt76.led_pin ? MT_LED_GPIO_MUX3 : MT_LED_GPIO_MUX2;
+ mt76_rmw_field(dev, i, MT_LED_GPIO_SEL_MASK, 4);
+ }
+
+ /* txs report queue */
+ mt76_rmw_field(dev, MT_DMA_TCRF1(0), MT_DMA_TCRF1_QIDX, 0);
+ mt76_rmw_field(dev, MT_DMA_TCRF1(1), MT_DMA_TCRF1_QIDX, 6);
+ mt76_rmw_field(dev, MT_DMA_TCRF1(2), MT_DMA_TCRF1_QIDX, 0);
+
+ /* rro module init */
+ mt7996_mcu_set_rro(dev, UNI_RRO_SET_PLATFORM_TYPE, 2);
+ mt7996_mcu_set_rro(dev, UNI_RRO_SET_BYPASS_MODE, 3);
+ mt7996_mcu_set_rro(dev, UNI_RRO_SET_TXFREE_PATH, 1);
+
+ mt7996_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(SET),
+ MCU_WA_PARAM_HW_PATH_HIF_VER,
+ HIF_TXD_V2_1, 0);
+
+ for (i = MT_BAND0; i <= MT_BAND2; i++)
+ mt7996_mac_init_band(dev, i);
+}
+
+static int mt7996_txbf_init(struct mt7996_dev *dev)
+{
+ int ret;
+
+ if (dev->dbdc_support) {
+ ret = mt7996_mcu_set_txbf(dev, BF_MOD_EN_CTRL);
+ if (ret)
+ return ret;
+ }
+
+ /* trigger sounding packets */
+ ret = mt7996_mcu_set_txbf(dev, BF_SOUNDING_ON);
+ if (ret)
+ return ret;
+
+ /* enable eBF */
+ return mt7996_mcu_set_txbf(dev, BF_HW_EN_UPDATE);
+}
+
+static int mt7996_register_phy(struct mt7996_dev *dev, struct mt7996_phy *phy,
+ enum mt76_band_id band)
+{
+ struct mt76_phy *mphy;
+ u32 mac_ofs, hif1_ofs = 0;
+ int ret;
+
+ if (band != MT_BAND1 && band != MT_BAND2)
+ return 0;
+
+ if ((band == MT_BAND1 && !dev->dbdc_support) ||
+ (band == MT_BAND2 && !dev->tbtc_support))
+ return 0;
+
+ if (phy)
+ return 0;
+
+ if (band == MT_BAND2 && dev->hif2)
+ hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0);
+
+ mphy = mt76_alloc_phy(&dev->mt76, sizeof(*phy), &mt7996_ops, band);
+ if (!mphy)
+ return -ENOMEM;
+
+ phy = mphy->priv;
+ phy->dev = dev;
+ phy->mt76 = mphy;
+ mphy->dev->phys[band] = mphy;
+
+ INIT_DELAYED_WORK(&mphy->mac_work, mt7996_mac_work);
+
+ ret = mt7996_eeprom_parse_hw_cap(dev, phy);
+ if (ret)
+ goto error;
+
+ mac_ofs = band == MT_BAND2 ? MT_EE_MAC_ADDR3 : MT_EE_MAC_ADDR2;
+ memcpy(mphy->macaddr, dev->mt76.eeprom.data + mac_ofs, ETH_ALEN);
+ /* Make the extra PHY MAC address local without overlapping with
+ * the usual MAC address allocation scheme on multiple virtual interfaces
+ */
+ if (!is_valid_ether_addr(mphy->macaddr)) {
+ memcpy(mphy->macaddr, dev->mt76.eeprom.data + MT_EE_MAC_ADDR,
+ ETH_ALEN);
+ mphy->macaddr[0] |= 2;
+ mphy->macaddr[0] ^= BIT(7);
+ if (band == MT_BAND2)
+ mphy->macaddr[0] ^= BIT(6);
+ }
+ mt76_eeprom_override(mphy);
+
+ /* init wiphy according to mphy and phy */
+ mt7996_init_wiphy(mphy->hw);
+ ret = mt76_connac_init_tx_queues(phy->mt76,
+ MT_TXQ_ID(band),
+ MT7996_TX_RING_SIZE,
+ MT_TXQ_RING_BASE(band) + hif1_ofs, 0);
+ if (ret)
+ goto error;
+
+ ret = mt76_register_phy(mphy, true, mt76_rates,
+ ARRAY_SIZE(mt76_rates));
+ if (ret)
+ goto error;
+
+ ret = mt7996_init_debugfs(phy);
+ if (ret)
+ goto error;
+
+ return 0;
+
+error:
+ mphy->dev->phys[band] = NULL;
+ ieee80211_free_hw(mphy->hw);
+ return ret;
+}
+
+static void
+mt7996_unregister_phy(struct mt7996_phy *phy, enum mt76_band_id band)
+{
+ struct mt76_phy *mphy;
+
+ if (!phy)
+ return;
+
+ mphy = phy->dev->mt76.phys[band];
+ mt76_unregister_phy(mphy);
+ ieee80211_free_hw(mphy->hw);
+ phy->dev->mt76.phys[band] = NULL;
+}
+
+static void mt7996_init_work(struct work_struct *work)
+{
+ struct mt7996_dev *dev = container_of(work, struct mt7996_dev,
+ init_work);
+
+ mt7996_mcu_set_eeprom(dev);
+ mt7996_mac_init(dev);
+ mt7996_init_txpower(dev, &dev->mphy.sband_2g.sband);
+ mt7996_init_txpower(dev, &dev->mphy.sband_5g.sband);
+ mt7996_init_txpower(dev, &dev->mphy.sband_6g.sband);
+ mt7996_txbf_init(dev);
+}
+
+void mt7996_wfsys_reset(struct mt7996_dev *dev)
+{
+ mt76_set(dev, MT_WF_SUBSYS_RST, 0x1);
+ msleep(20);
+
+ mt76_clear(dev, MT_WF_SUBSYS_RST, 0x1);
+ msleep(20);
+}
+
+static int mt7996_init_hardware(struct mt7996_dev *dev)
+{
+ int ret, idx;
+
+ mt76_wr(dev, MT_INT_SOURCE_CSR, ~0);
+
+ INIT_WORK(&dev->init_work, mt7996_init_work);
+
+ dev->dbdc_support = true;
+ dev->tbtc_support = true;
+
+ ret = mt7996_dma_init(dev);
+ if (ret)
+ return ret;
+
+ set_bit(MT76_STATE_INITIALIZED, &dev->mphy.state);
+
+ ret = mt7996_mcu_init(dev);
+ if (ret)
+ return ret;
+
+ ret = mt7996_eeprom_init(dev);
+ if (ret < 0)
+ return ret;
+
+ /* Beacon and mgmt frames should occupy wcid 0 */
+ idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT7996_WTBL_STA);
+ if (idx)
+ return -ENOSPC;
+
+ dev->mt76.global_wcid.idx = idx;
+ dev->mt76.global_wcid.hw_key_idx = -1;
+ dev->mt76.global_wcid.tx_info |= MT_WCID_TX_INFO_SET;
+ rcu_assign_pointer(dev->mt76.wcid[idx], &dev->mt76.global_wcid);
+
+ return 0;
+}
+
+void mt7996_set_stream_vht_txbf_caps(struct mt7996_phy *phy)
+{
+ int sts;
+ u32 *cap;
+
+ if (!phy->mt76->cap.has_5ghz)
+ return;
+
+ sts = hweight16(phy->mt76->chainmask);
+ cap = &phy->mt76->sband_5g.sband.vht_cap.cap;
+
+ *cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
+ IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE |
+ (3 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT);
+
+ *cap &= ~(IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK |
+ IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE |
+ IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE);
+
+ if (sts < 2)
+ return;
+
+ *cap |= IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE |
+ IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE |
+ FIELD_PREP(IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK, sts - 1);
+}
+
+static void
+mt7996_set_stream_he_txbf_caps(struct mt7996_phy *phy,
+ struct ieee80211_sta_he_cap *he_cap, int vif)
+{
+ struct ieee80211_he_cap_elem *elem = &he_cap->he_cap_elem;
+ int sts = hweight16(phy->mt76->chainmask);
+ u8 c;
+
+#ifdef CONFIG_MAC80211_MESH
+ if (vif == NL80211_IFTYPE_MESH_POINT)
+ return;
+#endif
+
+ elem->phy_cap_info[3] &= ~IEEE80211_HE_PHY_CAP3_SU_BEAMFORMER;
+ elem->phy_cap_info[4] &= ~IEEE80211_HE_PHY_CAP4_MU_BEAMFORMER;
+
+ c = IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK |
+ IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_MASK;
+ elem->phy_cap_info[5] &= ~c;
+
+ c = IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMING_FB |
+ IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMING_PARTIAL_BW_FB;
+ elem->phy_cap_info[6] &= ~c;
+
+ 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;
+ elem->phy_cap_info[2] |= c;
+
+ c = IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE |
+ IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_4 |
+ IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_4;
+ elem->phy_cap_info[4] |= c;
+
+ /* do not support NG16 due to spec D4.0 changes subcarrier idx */
+ c = IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_42_SU |
+ IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_75_MU;
+
+ if (vif == NL80211_IFTYPE_STATION)
+ c |= IEEE80211_HE_PHY_CAP6_PARTIAL_BANDWIDTH_DL_MUMIMO;
+
+ elem->phy_cap_info[6] |= c;
+
+ if (sts < 2)
+ return;
+
+ /* the maximum cap is 4 x 3, (Nr, Nc) = (3, 2) */
+ elem->phy_cap_info[7] |= min_t(int, sts - 1, 2) << 3;
+
+ if (vif != NL80211_IFTYPE_AP)
+ return;
+
+ elem->phy_cap_info[3] |= IEEE80211_HE_PHY_CAP3_SU_BEAMFORMER;
+ elem->phy_cap_info[4] |= IEEE80211_HE_PHY_CAP4_MU_BEAMFORMER;
+
+ c = FIELD_PREP(IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK,
+ sts - 1) |
+ FIELD_PREP(IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_MASK,
+ sts - 1);
+ elem->phy_cap_info[5] |= c;
+
+ c = IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMING_FB |
+ IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMING_PARTIAL_BW_FB;
+ elem->phy_cap_info[6] |= c;
+
+ c = IEEE80211_HE_PHY_CAP7_STBC_TX_ABOVE_80MHZ |
+ IEEE80211_HE_PHY_CAP7_STBC_RX_ABOVE_80MHZ;
+ elem->phy_cap_info[7] |= c;
+}
+
+static void
+mt7996_gen_ppe_thresh(u8 *he_ppet, int nss)
+{
+ u8 i, ppet_bits, ppet_size, ru_bit_mask = 0x7; /* HE80 */
+ static const u8 ppet16_ppet8_ru3_ru0[] = {0x1c, 0xc7, 0x71};
+
+ he_ppet[0] = FIELD_PREP(IEEE80211_PPE_THRES_NSS_MASK, nss - 1) |
+ FIELD_PREP(IEEE80211_PPE_THRES_RU_INDEX_BITMASK_MASK,
+ ru_bit_mask);
+
+ ppet_bits = IEEE80211_PPE_THRES_INFO_PPET_SIZE *
+ nss * hweight8(ru_bit_mask) * 2;
+ ppet_size = DIV_ROUND_UP(ppet_bits, 8);
+
+ for (i = 0; i < ppet_size - 1; i++)
+ he_ppet[i + 1] = ppet16_ppet8_ru3_ru0[i % 3];
+
+ he_ppet[i + 1] = ppet16_ppet8_ru3_ru0[i % 3] &
+ (0xff >> (8 - (ppet_bits - 1) % 8));
+}
+
+static int
+mt7996_init_he_caps(struct mt7996_phy *phy, enum nl80211_band band,
+ struct ieee80211_sband_iftype_data *data)
+{
+ int i, idx = 0, nss = hweight8(phy->mt76->antenna_mask);
+ u16 mcs_map = 0;
+
+ for (i = 0; i < 8; i++) {
+ if (i < nss)
+ mcs_map |= (IEEE80211_HE_MCS_SUPPORT_0_11 << (i * 2));
+ else
+ mcs_map |= (IEEE80211_HE_MCS_NOT_SUPPORTED << (i * 2));
+ }
+
+ for (i = 0; i < NUM_NL80211_IFTYPES; i++) {
+ struct ieee80211_sta_he_cap *he_cap = &data[idx].he_cap;
+ struct ieee80211_he_cap_elem *he_cap_elem =
+ &he_cap->he_cap_elem;
+ struct ieee80211_he_mcs_nss_supp *he_mcs =
+ &he_cap->he_mcs_nss_supp;
+
+ switch (i) {
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_AP:
+#ifdef CONFIG_MAC80211_MESH
+ case NL80211_IFTYPE_MESH_POINT:
+#endif
+ break;
+ default:
+ continue;
+ }
+
+ data[idx].types_mask = BIT(i);
+ he_cap->has_he = true;
+
+ he_cap_elem->mac_cap_info[0] =
+ IEEE80211_HE_MAC_CAP0_HTC_HE;
+ he_cap_elem->mac_cap_info[3] =
+ IEEE80211_HE_MAC_CAP3_OMI_CONTROL |
+ IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_3;
+ he_cap_elem->mac_cap_info[4] =
+ IEEE80211_HE_MAC_CAP4_AMSDU_IN_AMPDU;
+
+ if (band == NL80211_BAND_2GHZ)
+ he_cap_elem->phy_cap_info[0] =
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G;
+ else
+ he_cap_elem->phy_cap_info[0] =
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G |
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G;
+
+ he_cap_elem->phy_cap_info[1] =
+ IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD;
+ he_cap_elem->phy_cap_info[2] =
+ IEEE80211_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ |
+ IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ;
+
+ switch (i) {
+ case NL80211_IFTYPE_AP:
+ he_cap_elem->mac_cap_info[0] |=
+ IEEE80211_HE_MAC_CAP0_TWT_RES;
+ he_cap_elem->mac_cap_info[2] |=
+ IEEE80211_HE_MAC_CAP2_BSR;
+ he_cap_elem->mac_cap_info[4] |=
+ IEEE80211_HE_MAC_CAP4_BQR;
+ he_cap_elem->mac_cap_info[5] |=
+ IEEE80211_HE_MAC_CAP5_OM_CTRL_UL_MU_DATA_DIS_RX;
+ he_cap_elem->phy_cap_info[3] |=
+ IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_QPSK |
+ IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_QPSK;
+ he_cap_elem->phy_cap_info[6] |=
+ IEEE80211_HE_PHY_CAP6_PARTIAL_BW_EXT_RANGE |
+ IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT;
+ 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;
+ break;
+ case NL80211_IFTYPE_STATION:
+ he_cap_elem->mac_cap_info[1] |=
+ IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US;
+
+ if (band == NL80211_BAND_2GHZ)
+ he_cap_elem->phy_cap_info[0] |=
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_RU_MAPPING_IN_2G;
+ else
+ he_cap_elem->phy_cap_info[0] |=
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_RU_MAPPING_IN_5G;
+
+ he_cap_elem->phy_cap_info[1] |=
+ IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A |
+ IEEE80211_HE_PHY_CAP1_HE_LTF_AND_GI_FOR_HE_PPDUS_0_8US;
+ he_cap_elem->phy_cap_info[3] |=
+ IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_QPSK |
+ IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_QPSK;
+ he_cap_elem->phy_cap_info[6] |=
+ IEEE80211_HE_PHY_CAP6_TRIG_CQI_FB |
+ IEEE80211_HE_PHY_CAP6_PARTIAL_BW_EXT_RANGE |
+ IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT;
+ he_cap_elem->phy_cap_info[7] |=
+ IEEE80211_HE_PHY_CAP7_POWER_BOOST_FACTOR_SUPP |
+ IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF_AND_08_US_GI;
+ he_cap_elem->phy_cap_info[8] |=
+ IEEE80211_HE_PHY_CAP8_20MHZ_IN_40MHZ_HE_PPDU_IN_2G |
+ IEEE80211_HE_PHY_CAP8_20MHZ_IN_160MHZ_HE_PPDU |
+ IEEE80211_HE_PHY_CAP8_80MHZ_IN_160MHZ_HE_PPDU |
+ IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_484;
+ he_cap_elem->phy_cap_info[9] |=
+ IEEE80211_HE_PHY_CAP9_LONGER_THAN_16_SIGB_OFDM_SYM |
+ IEEE80211_HE_PHY_CAP9_NON_TRIGGERED_CQI_FEEDBACK |
+ IEEE80211_HE_PHY_CAP9_TX_1024_QAM_LESS_THAN_242_TONE_RU |
+ IEEE80211_HE_PHY_CAP9_RX_1024_QAM_LESS_THAN_242_TONE_RU |
+ IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_COMP_SIGB |
+ IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_NON_COMP_SIGB;
+ break;
+ }
+
+ he_mcs->rx_mcs_80 = cpu_to_le16(mcs_map);
+ he_mcs->tx_mcs_80 = cpu_to_le16(mcs_map);
+ he_mcs->rx_mcs_160 = cpu_to_le16(mcs_map);
+ he_mcs->tx_mcs_160 = cpu_to_le16(mcs_map);
+ he_mcs->rx_mcs_80p80 = cpu_to_le16(mcs_map);
+ he_mcs->tx_mcs_80p80 = cpu_to_le16(mcs_map);
+
+ mt7996_set_stream_he_txbf_caps(phy, he_cap, i);
+
+ memset(he_cap->ppe_thres, 0, sizeof(he_cap->ppe_thres));
+ if (he_cap_elem->phy_cap_info[6] &
+ IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) {
+ mt7996_gen_ppe_thresh(he_cap->ppe_thres, nss);
+ } else {
+ he_cap_elem->phy_cap_info[9] |=
+ IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_16US;
+ }
+
+ if (band == NL80211_BAND_6GHZ) {
+ u16 cap = IEEE80211_HE_6GHZ_CAP_TX_ANTPAT_CONS |
+ IEEE80211_HE_6GHZ_CAP_RX_ANTPAT_CONS;
+
+ cap |= u16_encode_bits(IEEE80211_HT_MPDU_DENSITY_2,
+ IEEE80211_HE_6GHZ_CAP_MIN_MPDU_START) |
+ u16_encode_bits(IEEE80211_VHT_MAX_AMPDU_1024K,
+ IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP) |
+ u16_encode_bits(IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454,
+ IEEE80211_HE_6GHZ_CAP_MAX_MPDU_LEN);
+
+ data[idx].he_6ghz_capa.capa = cpu_to_le16(cap);
+ }
+
+ idx++;
+ }
+
+ return idx;
+}
+
+void mt7996_set_stream_he_caps(struct mt7996_phy *phy)
+{
+ struct ieee80211_sband_iftype_data *data;
+ struct ieee80211_supported_band *band;
+ int n;
+
+ if (phy->mt76->cap.has_2ghz) {
+ data = phy->iftype[NL80211_BAND_2GHZ];
+ n = mt7996_init_he_caps(phy, NL80211_BAND_2GHZ, data);
+
+ band = &phy->mt76->sband_2g.sband;
+ band->iftype_data = data;
+ band->n_iftype_data = n;
+ }
+
+ if (phy->mt76->cap.has_5ghz) {
+ data = phy->iftype[NL80211_BAND_5GHZ];
+ n = mt7996_init_he_caps(phy, NL80211_BAND_5GHZ, data);
+
+ band = &phy->mt76->sband_5g.sband;
+ band->iftype_data = data;
+ band->n_iftype_data = n;
+ }
+
+ if (phy->mt76->cap.has_6ghz) {
+ data = phy->iftype[NL80211_BAND_6GHZ];
+ n = mt7996_init_he_caps(phy, NL80211_BAND_6GHZ, data);
+
+ band = &phy->mt76->sband_6g.sband;
+ band->iftype_data = data;
+ band->n_iftype_data = n;
+ }
+}
+
+int mt7996_register_device(struct mt7996_dev *dev)
+{
+ struct ieee80211_hw *hw = mt76_hw(dev);
+ int ret;
+
+ dev->phy.dev = dev;
+ dev->phy.mt76 = &dev->mt76.phy;
+ dev->mt76.phy.priv = &dev->phy;
+ INIT_WORK(&dev->rc_work, mt7996_mac_sta_rc_work);
+ INIT_DELAYED_WORK(&dev->mphy.mac_work, mt7996_mac_work);
+ INIT_LIST_HEAD(&dev->sta_rc_list);
+ INIT_LIST_HEAD(&dev->sta_poll_list);
+ INIT_LIST_HEAD(&dev->twt_list);
+ spin_lock_init(&dev->sta_poll_lock);
+
+ init_waitqueue_head(&dev->reset_wait);
+ INIT_WORK(&dev->reset_work, mt7996_mac_reset_work);
+
+ ret = mt7996_init_hardware(dev);
+ if (ret)
+ return ret;
+
+ mt7996_init_wiphy(hw);
+
+ /* init led callbacks */
+ if (IS_ENABLED(CONFIG_MT76_LEDS)) {
+ dev->mt76.led_cdev.brightness_set = mt7996_led_set_brightness;
+ dev->mt76.led_cdev.blink_set = mt7996_led_set_blink;
+ }
+
+ ret = mt76_register_device(&dev->mt76, true, mt76_rates,
+ ARRAY_SIZE(mt76_rates));
+ if (ret)
+ return ret;
+
+ ieee80211_queue_work(mt76_hw(dev), &dev->init_work);
+
+ ret = mt7996_register_phy(dev, mt7996_phy2(dev), MT_BAND1);
+ if (ret)
+ return ret;
+
+ ret = mt7996_register_phy(dev, mt7996_phy3(dev), MT_BAND2);
+ if (ret)
+ return ret;
+
+ return mt7996_init_debugfs(&dev->phy);
+}
+
+void mt7996_unregister_device(struct mt7996_dev *dev)
+{
+ mt7996_unregister_phy(mt7996_phy3(dev), MT_BAND2);
+ mt7996_unregister_phy(mt7996_phy2(dev), MT_BAND1);
+ mt76_unregister_device(&dev->mt76);
+ mt7996_mcu_exit(dev);
+ mt7996_tx_token_put(dev);
+ mt7996_dma_cleanup(dev);
+ tasklet_disable(&dev->irq_tasklet);
+
+ mt76_free_device(&dev->mt76);
+}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c
new file mode 100644
index 000000000000..0b3e28748e76
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c
@@ -0,0 +1,2498 @@
+// SPDX-License-Identifier: ISC
+/*
+ * Copyright (C) 2022 MediaTek Inc.
+ */
+
+#include <linux/etherdevice.h>
+#include <linux/timekeeping.h>
+#include "mt7996.h"
+#include "../dma.h"
+#include "mac.h"
+#include "mcu.h"
+
+#define to_rssi(field, rcpi) ((FIELD_GET(field, rcpi) - 220) / 2)
+
+#define HE_BITS(f) cpu_to_le16(IEEE80211_RADIOTAP_HE_##f)
+#define HE_PREP(f, m, v) le16_encode_bits(le32_get_bits(v, MT_CRXV_HE_##m),\
+ IEEE80211_RADIOTAP_HE_##f)
+
+static const struct mt7996_dfs_radar_spec etsi_radar_specs = {
+ .pulse_th = { 110, -10, -80, 40, 5200, 128, 5200 },
+ .radar_pattern = {
+ [5] = { 1, 0, 6, 32, 28, 0, 990, 5010, 17, 1, 1 },
+ [6] = { 1, 0, 9, 32, 28, 0, 615, 5010, 27, 1, 1 },
+ [7] = { 1, 0, 15, 32, 28, 0, 240, 445, 27, 1, 1 },
+ [8] = { 1, 0, 12, 32, 28, 0, 240, 510, 42, 1, 1 },
+ [9] = { 1, 1, 0, 0, 0, 0, 2490, 3343, 14, 0, 0, 12, 32, 28, { }, 126 },
+ [10] = { 1, 1, 0, 0, 0, 0, 2490, 3343, 14, 0, 0, 15, 32, 24, { }, 126 },
+ [11] = { 1, 1, 0, 0, 0, 0, 823, 2510, 14, 0, 0, 18, 32, 28, { }, 54 },
+ [12] = { 1, 1, 0, 0, 0, 0, 823, 2510, 14, 0, 0, 27, 32, 24, { }, 54 },
+ },
+};
+
+static const struct mt7996_dfs_radar_spec fcc_radar_specs = {
+ .pulse_th = { 110, -10, -80, 40, 5200, 128, 5200 },
+ .radar_pattern = {
+ [0] = { 1, 0, 8, 32, 28, 0, 508, 3076, 13, 1, 1 },
+ [1] = { 1, 0, 12, 32, 28, 0, 140, 240, 17, 1, 1 },
+ [2] = { 1, 0, 8, 32, 28, 0, 190, 510, 22, 1, 1 },
+ [3] = { 1, 0, 6, 32, 28, 0, 190, 510, 32, 1, 1 },
+ [4] = { 1, 0, 9, 255, 28, 0, 323, 343, 13, 1, 32 },
+ },
+};
+
+static const struct mt7996_dfs_radar_spec jp_radar_specs = {
+ .pulse_th = { 110, -10, -80, 40, 5200, 128, 5200 },
+ .radar_pattern = {
+ [0] = { 1, 0, 8, 32, 28, 0, 508, 3076, 13, 1, 1 },
+ [1] = { 1, 0, 12, 32, 28, 0, 140, 240, 17, 1, 1 },
+ [2] = { 1, 0, 8, 32, 28, 0, 190, 510, 22, 1, 1 },
+ [3] = { 1, 0, 6, 32, 28, 0, 190, 510, 32, 1, 1 },
+ [4] = { 1, 0, 9, 255, 28, 0, 323, 343, 13, 1, 32 },
+ [13] = { 1, 0, 7, 32, 28, 0, 3836, 3856, 14, 1, 1 },
+ [14] = { 1, 0, 6, 32, 28, 0, 615, 5010, 110, 1, 1 },
+ [15] = { 1, 1, 0, 0, 0, 0, 15, 5010, 110, 0, 0, 12, 32, 28 },
+ },
+};
+
+static struct mt76_wcid *mt7996_rx_get_wcid(struct mt7996_dev *dev,
+ u16 idx, bool unicast)
+{
+ struct mt7996_sta *sta;
+ struct mt76_wcid *wcid;
+
+ if (idx >= ARRAY_SIZE(dev->mt76.wcid))
+ return NULL;
+
+ wcid = rcu_dereference(dev->mt76.wcid[idx]);
+ if (unicast || !wcid)
+ return wcid;
+
+ if (!wcid->sta)
+ return NULL;
+
+ sta = container_of(wcid, struct mt7996_sta, wcid);
+ if (!sta->vif)
+ return NULL;
+
+ return &sta->vif->sta.wcid;
+}
+
+void mt7996_sta_ps(struct mt76_dev *mdev, struct ieee80211_sta *sta, bool ps)
+{
+}
+
+bool mt7996_mac_wtbl_update(struct mt7996_dev *dev, int idx, u32 mask)
+{
+ mt76_rmw(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_WLAN_IDX,
+ FIELD_PREP(MT_WTBL_UPDATE_WLAN_IDX, idx) | mask);
+
+ return mt76_poll(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_BUSY,
+ 0, 5000);
+}
+
+u32 mt7996_mac_wtbl_lmac_addr(struct mt7996_dev *dev, u16 wcid, u8 dw)
+{
+ mt76_wr(dev, MT_WTBLON_TOP_WDUCR,
+ FIELD_PREP(MT_WTBLON_TOP_WDUCR_GROUP, (wcid >> 7)));
+
+ return MT_WTBL_LMAC_OFFS(wcid, dw);
+}
+
+static void mt7996_mac_sta_poll(struct mt7996_dev *dev)
+{
+ static const u8 ac_to_tid[] = {
+ [IEEE80211_AC_BE] = 0,
+ [IEEE80211_AC_BK] = 1,
+ [IEEE80211_AC_VI] = 4,
+ [IEEE80211_AC_VO] = 6
+ };
+ struct ieee80211_sta *sta;
+ struct mt7996_sta *msta;
+ struct rate_info *rate;
+ u32 tx_time[IEEE80211_NUM_ACS], rx_time[IEEE80211_NUM_ACS];
+ LIST_HEAD(sta_poll_list);
+ int i;
+
+ spin_lock_bh(&dev->sta_poll_lock);
+ list_splice_init(&dev->sta_poll_list, &sta_poll_list);
+ spin_unlock_bh(&dev->sta_poll_lock);
+
+ rcu_read_lock();
+
+ while (true) {
+ bool clear = false;
+ u32 addr, val;
+ u16 idx;
+ s8 rssi[4];
+ u8 bw;
+
+ spin_lock_bh(&dev->sta_poll_lock);
+ if (list_empty(&sta_poll_list)) {
+ spin_unlock_bh(&dev->sta_poll_lock);
+ break;
+ }
+ msta = list_first_entry(&sta_poll_list,
+ struct mt7996_sta, poll_list);
+ list_del_init(&msta->poll_list);
+ spin_unlock_bh(&dev->sta_poll_lock);
+
+ idx = msta->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];
+
+ msta->airtime_ac[i] = mt76_rr(dev, addr);
+ msta->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;
+
+ if ((tx_last | rx_last) & BIT(30))
+ clear = true;
+
+ addr += 8;
+ }
+
+ if (clear) {
+ mt7996_mac_wtbl_update(dev, idx,
+ MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
+ memset(msta->airtime_ac, 0, sizeof(msta->airtime_ac));
+ }
+
+ if (!msta->wcid.sta)
+ continue;
+
+ sta = container_of((void *)msta, struct ieee80211_sta,
+ drv_priv);
+ for (i = 0; i < IEEE80211_NUM_ACS; i++) {
+ u8 q = mt76_connac_lmac_mapping(i);
+ u32 tx_cur = tx_time[q];
+ u32 rx_cur = rx_time[q];
+ u8 tid = ac_to_tid[i];
+
+ if (!tx_cur && !rx_cur)
+ continue;
+
+ ieee80211_sta_register_airtime(sta, tid, tx_cur, rx_cur);
+ }
+
+ /* We don't support reading GI info from txs packets.
+ * For accurate tx status reporting and AQL improvement,
+ * we need to make sure that flags match so polling GI
+ * from per-sta counters directly.
+ */
+ rate = &msta->wcid.rate;
+
+ switch (rate->bw) {
+ case RATE_INFO_BW_160:
+ bw = IEEE80211_STA_RX_BW_160;
+ break;
+ case RATE_INFO_BW_80:
+ bw = IEEE80211_STA_RX_BW_80;
+ break;
+ case RATE_INFO_BW_40:
+ bw = IEEE80211_STA_RX_BW_40;
+ break;
+ default:
+ bw = IEEE80211_STA_RX_BW_20;
+ break;
+ }
+
+ addr = mt7996_mac_wtbl_lmac_addr(dev, idx, 6);
+ val = mt76_rr(dev, addr);
+ if (rate->flags & RATE_INFO_FLAGS_HE_MCS) {
+ u8 offs = 24 + 2 * bw;
+
+ rate->he_gi = (val & (0x3 << offs)) >> offs;
+ } else if (rate->flags &
+ (RATE_INFO_FLAGS_VHT_MCS | RATE_INFO_FLAGS_MCS)) {
+ if (val & BIT(12 + bw))
+ rate->flags |= RATE_INFO_FLAGS_SHORT_GI;
+ else
+ rate->flags &= ~RATE_INFO_FLAGS_SHORT_GI;
+ }
+
+ /* get signal strength of resp frames (CTS/BA/ACK) */
+ addr = mt7996_mac_wtbl_lmac_addr(dev, idx, 34);
+ val = mt76_rr(dev, addr);
+
+ rssi[0] = to_rssi(GENMASK(7, 0), val);
+ rssi[1] = to_rssi(GENMASK(15, 8), val);
+ rssi[2] = to_rssi(GENMASK(23, 16), val);
+ rssi[3] = to_rssi(GENMASK(31, 14), val);
+
+ msta->ack_signal =
+ mt76_rx_signal(msta->vif->phy->mt76->antenna_mask, rssi);
+
+ ewma_avg_signal_add(&msta->avg_ack_signal, -msta->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->sta.wcid.idx, 5);
+ if (enable)
+ mt76_set(dev, addr, BIT(5));
+ else
+ mt76_clear(dev, addr, BIT(5));
+}
+
+static void
+mt7996_mac_decode_he_radiotap_ru(struct mt76_rx_status *status,
+ struct ieee80211_radiotap_he *he,
+ __le32 *rxv)
+{
+ u32 ru_h, ru_l;
+ u8 ru, offs = 0;
+
+ ru_l = le32_get_bits(rxv[0], MT_PRXV_HE_RU_ALLOC_L);
+ ru_h = le32_get_bits(rxv[1], MT_PRXV_HE_RU_ALLOC_H);
+ ru = (u8)(ru_l | ru_h << 4);
+
+ status->bw = RATE_INFO_BW_HE_RU;
+
+ switch (ru) {
+ case 0 ... 36:
+ status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_26;
+ offs = ru;
+ break;
+ case 37 ... 52:
+ status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_52;
+ offs = ru - 37;
+ break;
+ case 53 ... 60:
+ status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_106;
+ offs = ru - 53;
+ break;
+ case 61 ... 64:
+ status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_242;
+ offs = ru - 61;
+ break;
+ case 65 ... 66:
+ status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_484;
+ offs = ru - 65;
+ break;
+ case 67:
+ status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_996;
+ break;
+ case 68:
+ status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_2x996;
+ break;
+ }
+
+ he->data1 |= HE_BITS(DATA1_BW_RU_ALLOC_KNOWN);
+ he->data2 |= HE_BITS(DATA2_RU_OFFSET_KNOWN) |
+ le16_encode_bits(offs,
+ IEEE80211_RADIOTAP_HE_DATA2_RU_OFFSET);
+}
+
+static void
+mt7996_mac_decode_he_mu_radiotap(struct sk_buff *skb, __le32 *rxv)
+{
+ struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb;
+ static const struct ieee80211_radiotap_he_mu mu_known = {
+ .flags1 = HE_BITS(MU_FLAGS1_SIG_B_MCS_KNOWN) |
+ HE_BITS(MU_FLAGS1_SIG_B_DCM_KNOWN) |
+ HE_BITS(MU_FLAGS1_CH1_RU_KNOWN) |
+ HE_BITS(MU_FLAGS1_SIG_B_SYMS_USERS_KNOWN),
+ .flags2 = HE_BITS(MU_FLAGS2_BW_FROM_SIG_A_BW_KNOWN),
+ };
+ struct ieee80211_radiotap_he_mu *he_mu = NULL;
+
+ status->flag |= RX_FLAG_RADIOTAP_HE_MU;
+
+ he_mu = skb_push(skb, sizeof(mu_known));
+ memcpy(he_mu, &mu_known, sizeof(mu_known));
+
+#define MU_PREP(f, v) le16_encode_bits(v, IEEE80211_RADIOTAP_HE_MU_##f)
+
+ he_mu->flags1 |= MU_PREP(FLAGS1_SIG_B_MCS, status->rate_idx);
+ if (status->he_dcm)
+ he_mu->flags1 |= MU_PREP(FLAGS1_SIG_B_DCM, status->he_dcm);
+
+ he_mu->flags2 |= MU_PREP(FLAGS2_BW_FROM_SIG_A_BW, status->bw) |
+ MU_PREP(FLAGS2_SIG_B_SYMS_USERS,
+ le32_get_bits(rxv[2], MT_CRXV_HE_NUM_USER));
+
+ he_mu->ru_ch1[0] = le32_get_bits(rxv[3], MT_CRXV_HE_RU0);
+
+ if (status->bw >= RATE_INFO_BW_40) {
+ he_mu->flags1 |= HE_BITS(MU_FLAGS1_CH2_RU_KNOWN);
+ he_mu->ru_ch2[0] = le32_get_bits(rxv[3], MT_CRXV_HE_RU1);
+ }
+
+ if (status->bw >= RATE_INFO_BW_80) {
+ he_mu->ru_ch1[1] = le32_get_bits(rxv[3], MT_CRXV_HE_RU2);
+ he_mu->ru_ch2[1] = le32_get_bits(rxv[3], MT_CRXV_HE_RU3);
+ }
+}
+
+static void
+mt7996_mac_decode_he_radiotap(struct sk_buff *skb, __le32 *rxv, u8 mode)
+{
+ struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb;
+ static const struct ieee80211_radiotap_he known = {
+ .data1 = HE_BITS(DATA1_DATA_MCS_KNOWN) |
+ HE_BITS(DATA1_DATA_DCM_KNOWN) |
+ HE_BITS(DATA1_STBC_KNOWN) |
+ HE_BITS(DATA1_CODING_KNOWN) |
+ HE_BITS(DATA1_LDPC_XSYMSEG_KNOWN) |
+ HE_BITS(DATA1_DOPPLER_KNOWN) |
+ HE_BITS(DATA1_SPTL_REUSE_KNOWN) |
+ HE_BITS(DATA1_BSS_COLOR_KNOWN),
+ .data2 = HE_BITS(DATA2_GI_KNOWN) |
+ HE_BITS(DATA2_TXBF_KNOWN) |
+ HE_BITS(DATA2_PE_DISAMBIG_KNOWN) |
+ HE_BITS(DATA2_TXOP_KNOWN),
+ };
+ struct ieee80211_radiotap_he *he = NULL;
+ u32 ltf_size = le32_get_bits(rxv[2], MT_CRXV_HE_LTF_SIZE) + 1;
+
+ status->flag |= RX_FLAG_RADIOTAP_HE;
+
+ he = skb_push(skb, sizeof(known));
+ memcpy(he, &known, sizeof(known));
+
+ he->data3 = HE_PREP(DATA3_BSS_COLOR, BSS_COLOR, rxv[14]) |
+ HE_PREP(DATA3_LDPC_XSYMSEG, LDPC_EXT_SYM, rxv[2]);
+ he->data4 = HE_PREP(DATA4_SU_MU_SPTL_REUSE, SR_MASK, rxv[11]);
+ he->data5 = HE_PREP(DATA5_PE_DISAMBIG, PE_DISAMBIG, rxv[2]) |
+ le16_encode_bits(ltf_size,
+ IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE);
+ if (le32_to_cpu(rxv[0]) & MT_PRXV_TXBF)
+ he->data5 |= HE_BITS(DATA5_TXBF);
+ he->data6 = HE_PREP(DATA6_TXOP, TXOP_DUR, rxv[14]) |
+ HE_PREP(DATA6_DOPPLER, DOPPLER, rxv[14]);
+
+ switch (mode) {
+ case MT_PHY_TYPE_HE_SU:
+ he->data1 |= HE_BITS(DATA1_FORMAT_SU) |
+ HE_BITS(DATA1_UL_DL_KNOWN) |
+ HE_BITS(DATA1_BEAM_CHANGE_KNOWN) |
+ HE_BITS(DATA1_BW_RU_ALLOC_KNOWN);
+
+ he->data3 |= HE_PREP(DATA3_BEAM_CHANGE, BEAM_CHNG, rxv[14]) |
+ HE_PREP(DATA3_UL_DL, UPLINK, rxv[2]);
+ break;
+ case MT_PHY_TYPE_HE_EXT_SU:
+ he->data1 |= HE_BITS(DATA1_FORMAT_EXT_SU) |
+ HE_BITS(DATA1_UL_DL_KNOWN) |
+ HE_BITS(DATA1_BW_RU_ALLOC_KNOWN);
+
+ he->data3 |= HE_PREP(DATA3_UL_DL, UPLINK, rxv[2]);
+ break;
+ case MT_PHY_TYPE_HE_MU:
+ he->data1 |= HE_BITS(DATA1_FORMAT_MU) |
+ HE_BITS(DATA1_UL_DL_KNOWN);
+
+ he->data3 |= HE_PREP(DATA3_UL_DL, UPLINK, rxv[2]);
+ he->data4 |= HE_PREP(DATA4_MU_STA_ID, MU_AID, rxv[7]);
+
+ mt7996_mac_decode_he_radiotap_ru(status, he, rxv);
+ mt7996_mac_decode_he_mu_radiotap(skb, rxv);
+ break;
+ case MT_PHY_TYPE_HE_TB:
+ he->data1 |= HE_BITS(DATA1_FORMAT_TRIG) |
+ HE_BITS(DATA1_SPTL_REUSE2_KNOWN) |
+ HE_BITS(DATA1_SPTL_REUSE3_KNOWN) |
+ HE_BITS(DATA1_SPTL_REUSE4_KNOWN);
+
+ he->data4 |= HE_PREP(DATA4_TB_SPTL_REUSE1, SR_MASK, rxv[11]) |
+ HE_PREP(DATA4_TB_SPTL_REUSE2, SR1_MASK, rxv[11]) |
+ HE_PREP(DATA4_TB_SPTL_REUSE3, SR2_MASK, rxv[11]) |
+ HE_PREP(DATA4_TB_SPTL_REUSE4, SR3_MASK, rxv[11]);
+
+ mt7996_mac_decode_he_radiotap_ru(status, he, rxv);
+ break;
+ default:
+ break;
+ }
+}
+
+/* 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)
+{
+ struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb;
+ struct ethhdr *eth_hdr = (struct ethhdr *)(skb->data + hdr_gap);
+ struct mt7996_sta *msta = (struct mt7996_sta *)status->wcid;
+ __le32 *rxd = (__le32 *)skb->data;
+ struct ieee80211_sta *sta;
+ struct ieee80211_vif *vif;
+ struct ieee80211_hdr hdr;
+ u16 frame_control;
+
+ if (le32_get_bits(rxd[3], MT_RXD3_NORMAL_ADDR_TYPE) !=
+ MT_RXD3_NORMAL_U2M)
+ return -EINVAL;
+
+ if (!(le32_to_cpu(rxd[1]) & MT_RXD1_NORMAL_GROUP_4))
+ return -EINVAL;
+
+ if (!msta || !msta->vif)
+ return -EINVAL;
+
+ sta = container_of((void *)msta, struct ieee80211_sta, drv_priv);
+ vif = container_of((void *)msta->vif, struct ieee80211_vif, drv_priv);
+
+ /* store the info from RXD and ethhdr to avoid being overridden */
+ frame_control = le32_get_bits(rxd[8], MT_RXD8_FRAME_CONTROL);
+ hdr.frame_control = cpu_to_le16(frame_control);
+ hdr.seq_ctrl = cpu_to_le16(le32_get_bits(rxd[10], MT_RXD10_SEQ_CTRL));
+ hdr.duration_id = 0;
+
+ ether_addr_copy(hdr.addr1, vif->addr);
+ ether_addr_copy(hdr.addr2, sta->addr);
+ switch (frame_control & (IEEE80211_FCTL_TODS |
+ IEEE80211_FCTL_FROMDS)) {
+ case 0:
+ ether_addr_copy(hdr.addr3, vif->bss_conf.bssid);
+ break;
+ case IEEE80211_FCTL_FROMDS:
+ ether_addr_copy(hdr.addr3, eth_hdr->h_source);
+ break;
+ case IEEE80211_FCTL_TODS:
+ ether_addr_copy(hdr.addr3, eth_hdr->h_dest);
+ break;
+ case IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS:
+ ether_addr_copy(hdr.addr3, eth_hdr->h_dest);
+ ether_addr_copy(hdr.addr4, eth_hdr->h_source);
+ break;
+ default:
+ break;
+ }
+
+ skb_pull(skb, hdr_gap + sizeof(struct ethhdr) - 2);
+ if (eth_hdr->h_proto == cpu_to_be16(ETH_P_AARP) ||
+ eth_hdr->h_proto == cpu_to_be16(ETH_P_IPX))
+ ether_addr_copy(skb_push(skb, ETH_ALEN), bridge_tunnel_header);
+ else if (be16_to_cpu(eth_hdr->h_proto) >= ETH_P_802_3_MIN)
+ ether_addr_copy(skb_push(skb, ETH_ALEN), rfc1042_header);
+ else
+ skb_pull(skb, 2);
+
+ if (ieee80211_has_order(hdr.frame_control))
+ memcpy(skb_push(skb, IEEE80211_HT_CTL_LEN), &rxd[11],
+ IEEE80211_HT_CTL_LEN);
+ if (ieee80211_is_data_qos(hdr.frame_control)) {
+ __le16 qos_ctrl;
+
+ qos_ctrl = cpu_to_le16(le32_get_bits(rxd[10], MT_RXD10_QOS_CTL));
+ memcpy(skb_push(skb, IEEE80211_QOS_CTL_LEN), &qos_ctrl,
+ IEEE80211_QOS_CTL_LEN);
+ }
+
+ if (ieee80211_has_a4(hdr.frame_control))
+ memcpy(skb_push(skb, sizeof(hdr)), &hdr, sizeof(hdr));
+ else
+ memcpy(skb_push(skb, sizeof(hdr) - 6), &hdr, sizeof(hdr) - 6);
+
+ return 0;
+}
+
+static int
+mt7996_mac_fill_rx_rate(struct mt7996_dev *dev,
+ struct mt76_rx_status *status,
+ struct ieee80211_supported_band *sband,
+ __le32 *rxv, u8 *mode)
+{
+ u32 v0, v2;
+ u8 stbc, gi, bw, dcm, nss;
+ int i, idx;
+ bool cck = false;
+
+ v0 = le32_to_cpu(rxv[0]);
+ v2 = le32_to_cpu(rxv[2]);
+
+ idx = FIELD_GET(MT_PRXV_TX_RATE, v0);
+ i = idx;
+ nss = FIELD_GET(MT_PRXV_NSTS, v0) + 1;
+
+ stbc = FIELD_GET(MT_PRXV_HT_STBC, v2);
+ gi = FIELD_GET(MT_PRXV_HT_SHORT_GI, v2);
+ *mode = FIELD_GET(MT_PRXV_TX_MODE, v2);
+ dcm = FIELD_GET(MT_PRXV_DCM, v2);
+ bw = FIELD_GET(MT_PRXV_FRAME_MODE, v2);
+
+ switch (*mode) {
+ case MT_PHY_TYPE_CCK:
+ cck = true;
+ fallthrough;
+ case MT_PHY_TYPE_OFDM:
+ i = mt76_get_rate(&dev->mt76, sband, i, cck);
+ break;
+ case MT_PHY_TYPE_HT_GF:
+ case MT_PHY_TYPE_HT:
+ status->encoding = RX_ENC_HT;
+ if (gi)
+ status->enc_flags |= RX_ENC_FLAG_SHORT_GI;
+ if (i > 31)
+ return -EINVAL;
+ break;
+ case MT_PHY_TYPE_VHT:
+ status->nss = nss;
+ status->encoding = RX_ENC_VHT;
+ if (gi)
+ status->enc_flags |= RX_ENC_FLAG_SHORT_GI;
+ if (i > 11)
+ return -EINVAL;
+ break;
+ case MT_PHY_TYPE_HE_MU:
+ case MT_PHY_TYPE_HE_SU:
+ case MT_PHY_TYPE_HE_EXT_SU:
+ case MT_PHY_TYPE_HE_TB:
+ status->nss = nss;
+ status->encoding = RX_ENC_HE;
+ i &= GENMASK(3, 0);
+
+ if (gi <= NL80211_RATE_INFO_HE_GI_3_2)
+ status->he_gi = gi;
+
+ status->he_dcm = dcm;
+ break;
+ default:
+ return -EINVAL;
+ }
+ status->rate_idx = i;
+
+ switch (bw) {
+ case IEEE80211_STA_RX_BW_20:
+ break;
+ case IEEE80211_STA_RX_BW_40:
+ if (*mode & MT_PHY_TYPE_HE_EXT_SU &&
+ (idx & MT_PRXV_TX_ER_SU_106T)) {
+ status->bw = RATE_INFO_BW_HE_RU;
+ status->he_ru =
+ NL80211_RATE_INFO_HE_RU_ALLOC_106;
+ } else {
+ status->bw = RATE_INFO_BW_40;
+ }
+ break;
+ case IEEE80211_STA_RX_BW_80:
+ status->bw = RATE_INFO_BW_80;
+ break;
+ case IEEE80211_STA_RX_BW_160:
+ status->bw = RATE_INFO_BW_160;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ status->enc_flags |= RX_ENC_FLAG_STBC_MASK * stbc;
+ if (*mode < MT_PHY_TYPE_HE_SU && gi)
+ status->enc_flags |= RX_ENC_FLAG_SHORT_GI;
+
+ return 0;
+}
+
+static int
+mt7996_mac_fill_rx(struct mt7996_dev *dev, struct sk_buff *skb)
+{
+ struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb;
+ struct mt76_phy *mphy = &dev->mt76.phy;
+ struct mt7996_phy *phy = &dev->phy;
+ struct ieee80211_supported_band *sband;
+ __le32 *rxd = (__le32 *)skb->data;
+ __le32 *rxv = NULL;
+ u32 rxd0 = le32_to_cpu(rxd[0]);
+ u32 rxd1 = le32_to_cpu(rxd[1]);
+ u32 rxd2 = le32_to_cpu(rxd[2]);
+ u32 rxd3 = le32_to_cpu(rxd[3]);
+ u32 rxd4 = le32_to_cpu(rxd[4]);
+ u32 csum_mask = MT_RXD0_NORMAL_IP_SUM | MT_RXD0_NORMAL_UDP_TCP_SUM;
+ u32 csum_status = *(u32 *)skb->cb;
+ bool unicast, insert_ccmp_hdr = false;
+ u8 remove_pad, amsdu_info, band_idx;
+ u8 mode = 0, qos_ctl = 0;
+ bool hdr_trans;
+ u16 hdr_gap;
+ u16 seq_ctrl = 0;
+ __le16 fc = 0;
+ int idx;
+
+ memset(status, 0, sizeof(*status));
+
+ band_idx = FIELD_GET(MT_RXD1_NORMAL_BAND_IDX, rxd1);
+ mphy = dev->mt76.phys[band_idx];
+ phy = mphy->priv;
+ status->phy_idx = mphy->band_idx;
+
+ if (!test_bit(MT76_STATE_RUNNING, &mphy->state))
+ return -EINVAL;
+
+ if (rxd2 & MT_RXD2_NORMAL_AMSDU_ERR)
+ return -EINVAL;
+
+ hdr_trans = rxd2 & MT_RXD2_NORMAL_HDR_TRANS;
+ if (hdr_trans && (rxd1 & MT_RXD1_NORMAL_CM))
+ return -EINVAL;
+
+ /* ICV error or CCMP/BIP/WPI MIC error */
+ if (rxd1 & MT_RXD1_NORMAL_ICV_ERR)
+ status->flag |= RX_FLAG_ONLY_MONITOR;
+
+ 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);
+
+ if (status->wcid) {
+ struct mt7996_sta *msta;
+
+ msta = container_of(status->wcid, struct mt7996_sta, wcid);
+ spin_lock_bh(&dev->sta_poll_lock);
+ if (list_empty(&msta->poll_list))
+ list_add_tail(&msta->poll_list, &dev->sta_poll_list);
+ spin_unlock_bh(&dev->sta_poll_lock);
+ }
+
+ status->freq = mphy->chandef.chan->center_freq;
+ status->band = mphy->chandef.chan->band;
+ if (status->band == NL80211_BAND_5GHZ)
+ sband = &mphy->sband_5g.sband;
+ else if (status->band == NL80211_BAND_6GHZ)
+ sband = &mphy->sband_6g.sband;
+ else
+ sband = &mphy->sband_2g.sband;
+
+ if (!sband->channels)
+ return -EINVAL;
+
+ if ((rxd0 & csum_mask) == csum_mask &&
+ !(csum_status & (BIT(0) | BIT(2) | BIT(3))))
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+ if (rxd1 & MT_RXD3_NORMAL_FCS_ERR)
+ status->flag |= RX_FLAG_FAILED_FCS_CRC;
+
+ if (rxd1 & MT_RXD1_NORMAL_TKIP_MIC_ERR)
+ status->flag |= RX_FLAG_MMIC_ERROR;
+
+ if (FIELD_GET(MT_RXD2_NORMAL_SEC_MODE, rxd2) != 0 &&
+ !(rxd1 & (MT_RXD1_NORMAL_CLM | MT_RXD1_NORMAL_CM))) {
+ status->flag |= RX_FLAG_DECRYPTED;
+ status->flag |= RX_FLAG_IV_STRIPPED;
+ status->flag |= RX_FLAG_MMIC_STRIPPED | RX_FLAG_MIC_STRIPPED;
+ }
+
+ remove_pad = FIELD_GET(MT_RXD2_NORMAL_HDR_OFFSET, rxd2);
+
+ if (rxd2 & MT_RXD2_NORMAL_MAX_LEN_ERROR)
+ return -EINVAL;
+
+ rxd += 8;
+ if (rxd1 & MT_RXD1_NORMAL_GROUP_4) {
+ u32 v0 = le32_to_cpu(rxd[0]);
+ u32 v2 = le32_to_cpu(rxd[2]);
+
+ fc = cpu_to_le16(FIELD_GET(MT_RXD8_FRAME_CONTROL, v0));
+ qos_ctl = FIELD_GET(MT_RXD10_QOS_CTL, v2);
+ seq_ctrl = FIELD_GET(MT_RXD10_SEQ_CTRL, v2);
+
+ rxd += 4;
+ if ((u8 *)rxd - skb->data >= skb->len)
+ return -EINVAL;
+ }
+
+ if (rxd1 & MT_RXD1_NORMAL_GROUP_1) {
+ u8 *data = (u8 *)rxd;
+
+ if (status->flag & RX_FLAG_DECRYPTED) {
+ switch (FIELD_GET(MT_RXD2_NORMAL_SEC_MODE, rxd2)) {
+ case MT_CIPHER_AES_CCMP:
+ case MT_CIPHER_CCMP_CCX:
+ case MT_CIPHER_CCMP_256:
+ insert_ccmp_hdr =
+ FIELD_GET(MT_RXD2_NORMAL_FRAG, rxd2);
+ fallthrough;
+ case MT_CIPHER_TKIP:
+ case MT_CIPHER_TKIP_NO_MIC:
+ case MT_CIPHER_GCMP:
+ case MT_CIPHER_GCMP_256:
+ status->iv[0] = data[5];
+ status->iv[1] = data[4];
+ status->iv[2] = data[3];
+ status->iv[3] = data[2];
+ status->iv[4] = data[1];
+ status->iv[5] = data[0];
+ break;
+ default:
+ break;
+ }
+ }
+ rxd += 4;
+ if ((u8 *)rxd - skb->data >= skb->len)
+ return -EINVAL;
+ }
+
+ if (rxd1 & MT_RXD1_NORMAL_GROUP_2) {
+ status->timestamp = le32_to_cpu(rxd[0]);
+ status->flag |= RX_FLAG_MACTIME_START;
+
+ if (!(rxd2 & MT_RXD2_NORMAL_NON_AMPDU)) {
+ status->flag |= RX_FLAG_AMPDU_DETAILS;
+
+ /* all subframes of an A-MPDU have the same timestamp */
+ if (phy->rx_ampdu_ts != status->timestamp) {
+ if (!++phy->ampdu_ref)
+ phy->ampdu_ref++;
+ }
+ phy->rx_ampdu_ts = status->timestamp;
+
+ status->ampdu_ref = phy->ampdu_ref;
+ }
+
+ rxd += 4;
+ if ((u8 *)rxd - skb->data >= skb->len)
+ return -EINVAL;
+ }
+
+ /* RXD Group 3 - P-RXV */
+ if (rxd1 & MT_RXD1_NORMAL_GROUP_3) {
+ u32 v3;
+ int ret;
+
+ rxv = rxd;
+ rxd += 4;
+ if ((u8 *)rxd - skb->data >= skb->len)
+ return -EINVAL;
+
+ v3 = le32_to_cpu(rxv[3]);
+
+ status->chains = mphy->antenna_mask;
+ status->chain_signal[0] = to_rssi(MT_PRXV_RCPI0, v3);
+ status->chain_signal[1] = to_rssi(MT_PRXV_RCPI1, v3);
+ status->chain_signal[2] = to_rssi(MT_PRXV_RCPI2, v3);
+ status->chain_signal[3] = to_rssi(MT_PRXV_RCPI3, v3);
+
+ /* RXD Group 5 - C-RXV */
+ if (rxd1 & MT_RXD1_NORMAL_GROUP_5) {
+ rxd += 24;
+ if ((u8 *)rxd - skb->data >= skb->len)
+ return -EINVAL;
+ }
+
+ ret = mt7996_mac_fill_rx_rate(dev, status, sband, rxv, &mode);
+ if (ret < 0)
+ return ret;
+ }
+
+ amsdu_info = FIELD_GET(MT_RXD4_NORMAL_PAYLOAD_FORMAT, rxd4);
+ status->amsdu = !!amsdu_info;
+ if (status->amsdu) {
+ status->first_amsdu = amsdu_info == MT_RXD4_FIRST_AMSDU_FRAME;
+ status->last_amsdu = amsdu_info == MT_RXD4_LAST_AMSDU_FRAME;
+ }
+
+ 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))
+ return -EINVAL;
+ hdr_trans = false;
+ } else {
+ int pad_start = 0;
+
+ skb_pull(skb, hdr_gap);
+ if (!hdr_trans && status->amsdu) {
+ pad_start = ieee80211_get_hdrlen_from_skb(skb);
+ } else if (hdr_trans && (rxd2 & MT_RXD2_NORMAL_HDR_TRANS_ERROR)) {
+ /* When header translation failure is indicated,
+ * the hardware will insert an extra 2-byte field
+ * containing the data length after the protocol
+ * type field.
+ */
+ pad_start = 12;
+ if (get_unaligned_be16(skb->data + pad_start) == ETH_P_8021Q)
+ pad_start += 4;
+ else
+ pad_start = 0;
+ }
+
+ if (pad_start) {
+ memmove(skb->data + 2, skb->data, pad_start);
+ skb_pull(skb, 2);
+ }
+ }
+
+ if (!hdr_trans) {
+ struct ieee80211_hdr *hdr;
+
+ if (insert_ccmp_hdr) {
+ u8 key_id = FIELD_GET(MT_RXD1_NORMAL_KEY_ID, rxd1);
+
+ mt76_insert_ccmp_hdr(skb, key_id);
+ }
+
+ hdr = mt76_skb_get_hdr(skb);
+ fc = hdr->frame_control;
+ if (ieee80211_is_data_qos(fc)) {
+ seq_ctrl = le16_to_cpu(hdr->seq_ctrl);
+ qos_ctl = *ieee80211_get_qos_ctl(hdr);
+ }
+ } else {
+ status->flag |= RX_FLAG_8023;
+ }
+
+ if (rxv && mode >= MT_PHY_TYPE_HE_SU && !(status->flag & RX_FLAG_8023))
+ mt7996_mac_decode_he_radiotap(skb, rxv, mode);
+
+ if (!status->wcid || !ieee80211_is_data_qos(fc))
+ return 0;
+
+ status->aggr = unicast &&
+ !ieee80211_is_qos_nullfunc(fc);
+ status->qos_ctl = qos_ctl;
+ status->seqno = IEEE80211_SEQ_TO_SN(seq_ctrl);
+
+ return 0;
+}
+
+static void
+mt7996_mac_write_txwi_8023(struct mt7996_dev *dev, __le32 *txwi,
+ struct sk_buff *skb, struct mt76_wcid *wcid)
+{
+ u8 tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK;
+ u8 fc_type, fc_stype;
+ u16 ethertype;
+ bool wmm = false;
+ u32 val;
+
+ if (wcid->sta) {
+ struct ieee80211_sta *sta;
+
+ sta = container_of((void *)wcid, struct ieee80211_sta, drv_priv);
+ wmm = sta->wme;
+ }
+
+ val = FIELD_PREP(MT_TXD1_HDR_FORMAT, MT_HDR_FORMAT_802_3) |
+ FIELD_PREP(MT_TXD1_TID, tid);
+
+ ethertype = get_unaligned_be16(&skb->data[12]);
+ if (ethertype >= ETH_P_802_3_MIN)
+ val |= MT_TXD1_ETH_802_3;
+
+ txwi[1] |= cpu_to_le32(val);
+
+ fc_type = IEEE80211_FTYPE_DATA >> 2;
+ fc_stype = wmm ? IEEE80211_STYPE_QOS_DATA >> 4 : 0;
+
+ val = FIELD_PREP(MT_TXD2_FRAME_TYPE, fc_type) |
+ FIELD_PREP(MT_TXD2_SUB_TYPE, fc_stype);
+
+ txwi[2] |= cpu_to_le32(val);
+}
+
+static void
+mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi,
+ struct sk_buff *skb, struct ieee80211_key_conf *key)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ bool multicast = is_multicast_ether_addr(hdr->addr1);
+ u8 tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK;
+ __le16 fc = hdr->frame_control;
+ 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)
+ tid = MT_TX_ADDBA;
+ 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,
+ ieee80211_get_hdrlen_from_skb(skb) / 2) |
+ FIELD_PREP(MT_TXD1_TID, tid);
+
+ if (!ieee80211_is_data(fc) || multicast ||
+ 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) {
+ val |= MT_TXD1_BIP;
+ txwi[3] &= ~cpu_to_le32(MT_TXD3_PROTECT_FRAME);
+ }
+
+ txwi[1] |= cpu_to_le32(val);
+
+ fc_type = (le16_to_cpu(fc) & IEEE80211_FCTL_FTYPE) >> 2;
+ fc_stype = (le16_to_cpu(fc) & IEEE80211_FCTL_STYPE) >> 4;
+
+ val = FIELD_PREP(MT_TXD2_FRAME_TYPE, fc_type) |
+ FIELD_PREP(MT_TXD2_SUB_TYPE, fc_stype);
+
+ txwi[2] |= cpu_to_le32(val);
+
+ txwi[3] |= cpu_to_le32(FIELD_PREP(MT_TXD3_BCM, multicast));
+ if (ieee80211_is_beacon(fc)) {
+ txwi[3] &= ~cpu_to_le32(MT_TXD3_SW_POWER_MGMT);
+ txwi[3] |= cpu_to_le32(MT_TXD3_REM_TX_COUNT);
+ }
+
+ if (info->flags & IEEE80211_TX_CTL_INJECTED) {
+ u16 seqno = le16_to_cpu(hdr->seq_ctrl);
+
+ if (ieee80211_is_back_req(hdr->frame_control)) {
+ struct ieee80211_bar *bar;
+
+ bar = (struct ieee80211_bar *)skb->data;
+ seqno = le16_to_cpu(bar->start_seq_num);
+ }
+
+ val = MT_TXD3_SN_VALID |
+ FIELD_PREP(MT_TXD3_SEQ, IEEE80211_SEQ_TO_SN(seqno));
+ txwi[3] |= cpu_to_le32(val);
+ txwi[3] &= ~cpu_to_le32(MT_TXD3_HW_AMSDU);
+ }
+}
+
+static u16
+mt7996_mac_tx_rate_val(struct mt76_phy *mphy, struct ieee80211_vif *vif,
+ bool beacon, bool mcast)
+{
+ u8 mode = 0, band = mphy->chandef.chan->band;
+ int rateidx = 0, mcast_rate;
+
+ if (beacon) {
+ struct cfg80211_bitrate_mask *mask;
+
+ mask = &vif->bss_conf.beacon_tx_rate;
+ if (hweight16(mask->control[band].he_mcs[0]) == 1) {
+ rateidx = ffs(mask->control[band].he_mcs[0]) - 1;
+ mode = MT_PHY_TYPE_HE_SU;
+ goto out;
+ } else if (hweight16(mask->control[band].vht_mcs[0]) == 1) {
+ rateidx = ffs(mask->control[band].vht_mcs[0]) - 1;
+ mode = MT_PHY_TYPE_VHT;
+ goto out;
+ } else if (hweight8(mask->control[band].ht_mcs[0]) == 1) {
+ rateidx = ffs(mask->control[band].ht_mcs[0]) - 1;
+ mode = MT_PHY_TYPE_HT;
+ goto out;
+ } else if (hweight32(mask->control[band].legacy) == 1) {
+ rateidx = ffs(mask->control[band].legacy) - 1;
+ goto legacy;
+ }
+ }
+
+ mcast_rate = vif->bss_conf.mcast_rate[band];
+ if (mcast && mcast_rate > 0)
+ rateidx = mcast_rate - 1;
+ else
+ rateidx = ffs(vif->bss_conf.basic_rates) - 1;
+
+legacy:
+ rateidx = mt76_calculate_default_rate(mphy, rateidx);
+ mode = rateidx >> 8;
+ rateidx &= GENMASK(7, 0);
+
+out:
+ return FIELD_PREP(MT_TX_RATE_IDX, rateidx) |
+ FIELD_PREP(MT_TX_RATE_MODE, mode);
+}
+
+void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi,
+ struct sk_buff *skb, struct mt76_wcid *wcid, int pid,
+ struct ieee80211_key_conf *key, u32 changed)
+{
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_vif *vif = info->control.vif;
+ struct mt76_phy *mphy = &dev->mphy;
+ 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;
+ u16 tx_count = 15;
+ u32 val;
+ bool beacon = !!(changed & (BSS_CHANGED_BEACON |
+ BSS_CHANGED_BEACON_ENABLED));
+ bool inband_disc = !!(changed & (BSS_CHANGED_UNSOL_BCAST_PROBE_RESP |
+ BSS_CHANGED_FILS_DISCOVERY));
+
+ if (vif) {
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+
+ omac_idx = mvif->mt76.omac_idx;
+ wmm_idx = mvif->mt76.wmm_idx;
+ band_idx = mvif->mt76.band_idx;
+ }
+
+ mphy = mt76_dev_phy(&dev->mt76, band_idx);
+
+ if (inband_disc) {
+ p_fmt = MT_TX_TYPE_FW;
+ q_idx = MT_LMAC_ALTX0;
+ } else if (beacon) {
+ p_fmt = MT_TX_TYPE_FW;
+ q_idx = MT_LMAC_BCN0;
+ } else if (skb_get_queue_mapping(skb) >= MT_TXQ_PSD) {
+ p_fmt = MT_TX_TYPE_CT;
+ q_idx = MT_LMAC_ALTX0;
+ } else {
+ p_fmt = MT_TX_TYPE_CT;
+ q_idx = wmm_idx * MT7996_MAX_WMM_SETS +
+ mt76_connac_lmac_mapping(skb_get_queue_mapping(skb));
+ }
+
+ val = FIELD_PREP(MT_TXD0_TX_BYTES, skb->len + MT_TXD_SIZE) |
+ FIELD_PREP(MT_TXD0_PKT_FMT, p_fmt) |
+ FIELD_PREP(MT_TXD0_Q_IDX, q_idx);
+ txwi[0] = cpu_to_le32(val);
+
+ val = FIELD_PREP(MT_TXD1_WLAN_IDX, wcid->idx) |
+ FIELD_PREP(MT_TXD1_OWN_MAC, omac_idx);
+
+ if (band_idx)
+ val |= FIELD_PREP(MT_TXD1_TGID, band_idx);
+
+ txwi[1] = cpu_to_le32(val);
+ txwi[2] = 0;
+
+ val = MT_TXD3_SW_POWER_MGMT |
+ FIELD_PREP(MT_TXD3_REM_TX_COUNT, tx_count);
+ if (key)
+ val |= MT_TXD3_PROTECT_FRAME;
+ if (info->flags & IEEE80211_TX_CTL_NO_ACK)
+ val |= MT_TXD3_NO_ACK;
+ if (wcid->amsdu)
+ val |= MT_TXD3_HW_AMSDU;
+
+ txwi[3] = cpu_to_le32(val);
+ txwi[4] = 0;
+
+ val = FIELD_PREP(MT_TXD5_PID, pid);
+ if (pid >= MT_PACKET_ID_FIRST)
+ val |= MT_TXD5_TX_STATUS_HOST;
+ txwi[5] = cpu_to_le32(val);
+
+ val = MT_TXD6_DIS_MAT | MT_TXD6_DAS |
+ FIELD_PREP(MT_TXD6_MSDU_CNT, 1);
+ txwi[6] = cpu_to_le32(val);
+ txwi[7] = 0;
+
+ if (is_8023)
+ mt7996_mac_write_txwi_8023(dev, txwi, skb, wcid);
+ else
+ mt7996_mac_write_txwi_80211(dev, txwi, skb, key);
+
+ if (txwi[1] & cpu_to_le32(MT_TXD1_FIXED_RATE)) {
+ /* Fixed rata is available just for 802.11 txd */
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ bool multicast = is_multicast_ether_addr(hdr->addr1);
+ u16 rate = mt7996_mac_tx_rate_val(mphy, vif, beacon, multicast);
+
+ /* fix to bw 20 */
+ val = MT_TXD6_FIXED_BW |
+ FIELD_PREP(MT_TXD6_BW, 0) |
+ FIELD_PREP(MT_TXD6_TX_RATE, rate);
+
+ txwi[6] |= cpu_to_le32(val);
+ txwi[3] |= cpu_to_le32(MT_TXD3_BA_DISABLE);
+ }
+}
+
+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;
+ struct ieee80211_vif *vif = info->control.vif;
+ struct mt76_txwi_cache *t;
+ struct mt7996_txp *txp;
+ int id, i, pid, nbuf = tx_info->nbuf - 1;
+ bool is_8023 = info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP;
+ u8 *txwi = (u8 *)txwi_ptr;
+
+ if (unlikely(tx_info->skb->len <= ETH_HLEN))
+ return -EINVAL;
+
+ if (!wcid)
+ wcid = &dev->mt76.global_wcid;
+
+ if (sta) {
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+
+ if (time_after(jiffies, msta->jiffies + HZ / 4)) {
+ info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
+ msta->jiffies = jiffies;
+ }
+ }
+
+ t = (struct mt76_txwi_cache *)(txwi + mdev->drv->txwi_size);
+ t->skb = tx_info->skb;
+
+ id = mt76_token_consume(mdev, &t);
+ if (id < 0)
+ return id;
+
+ pid = mt76_tx_status_skb_add(mdev, wcid, tx_info->skb);
+ 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, pid,
+ key, 0);
+
+ txp = (struct mt7996_txp *)(txwi + MT_TXD_SIZE);
+ for (i = 0; i < nbuf; i++) {
+ txp->buf[i] = cpu_to_le32(tx_info->buf[i + 1].addr);
+ txp->len[i] = cpu_to_le16(tx_info->buf[i + 1].len);
+ }
+ txp->nbuf = nbuf;
+
+ txp->flags = cpu_to_le16(MT_CT_INFO_FROM_HOST);
+
+ if (!is_8023 || pid >= MT_PACKET_ID_FIRST)
+ txp->flags |= cpu_to_le16(MT_CT_INFO_APPLY_TXD);
+
+ if (!key)
+ txp->flags |= cpu_to_le16(MT_CT_INFO_NONE_CIPHER_FRAME);
+
+ if (!is_8023 && ieee80211_is_mgmt(hdr->frame_control))
+ txp->flags |= cpu_to_le16(MT_CT_INFO_MGMT_FRAME);
+
+ if (vif) {
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+
+ txp->bss_idx = mvif->mt76.idx;
+ }
+
+ txp->token = cpu_to_le16(id);
+ if (test_bit(MT_WCID_FLAG_4ADDR, &wcid->flags))
+ txp->rept_wds_wcid = cpu_to_le16(wcid->idx);
+ else
+ txp->rept_wds_wcid = cpu_to_le16(0xfff);
+ tx_info->skb = DMA_DUMMY_DATA;
+
+ /* pass partial skb header to fw */
+ tx_info->buf[1].len = MT_CT_PARSE_LEN;
+ tx_info->buf[1].skip_unmap = true;
+ tx_info->nbuf = MT_CT_DMA_BUF_NUM;
+
+ return 0;
+}
+
+static void
+mt7996_tx_check_aggr(struct ieee80211_sta *sta, __le32 *txwi)
+{
+ struct mt7996_sta *msta;
+ u16 fc, tid;
+ u32 val;
+
+ if (!sta || !(sta->deflink.ht_cap.ht_supported || sta->deflink.he_cap.has_he))
+ return;
+
+ tid = le32_get_bits(txwi[1], MT_TXD1_TID);
+ if (tid >= 6) /* skip VO queue */
+ return;
+
+ val = le32_to_cpu(txwi[2]);
+ fc = FIELD_GET(MT_TXD2_FRAME_TYPE, val) << 2 |
+ FIELD_GET(MT_TXD2_SUB_TYPE, val) << 4;
+ if (unlikely(fc != (IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_DATA)))
+ return;
+
+ msta = (struct mt7996_sta *)sta->drv_priv;
+ if (!test_and_set_bit(tid, &msta->ampdu_state))
+ ieee80211_start_tx_ba_session(sta, tid, 0);
+}
+
+static void
+mt7996_txp_skb_unmap(struct mt76_dev *dev, struct mt76_txwi_cache *t)
+{
+ struct mt7996_txp *txp;
+ int i;
+
+ txp = mt7996_txwi_to_txp(dev, t);
+ for (i = 0; i < txp->nbuf; i++)
+ dma_unmap_single(dev->dev, le32_to_cpu(txp->buf[i]),
+ le16_to_cpu(txp->len[i]), DMA_TO_DEVICE);
+}
+
+static void
+mt7996_txwi_free(struct mt7996_dev *dev, struct mt76_txwi_cache *t,
+ struct ieee80211_sta *sta, struct list_head *free_list)
+{
+ struct mt76_dev *mdev = &dev->mt76;
+ struct mt76_wcid *wcid;
+ __le32 *txwi;
+ u16 wcid_idx;
+
+ mt7996_txp_skb_unmap(mdev, t);
+ if (!t->skb)
+ goto out;
+
+ txwi = (__le32 *)mt76_get_txwi_ptr(mdev, t);
+ if (sta) {
+ wcid = (struct mt76_wcid *)sta->drv_priv;
+ wcid_idx = wcid->idx;
+
+ if (likely(t->skb->protocol != cpu_to_be16(ETH_P_PAE)))
+ mt7996_tx_check_aggr(sta, txwi);
+ } else {
+ wcid_idx = le32_get_bits(txwi[1], MT_TXD1_WLAN_IDX);
+ }
+
+ __mt76_tx_complete_skb(mdev, wcid_idx, t->skb, free_list);
+
+out:
+ t->skb = NULL;
+ mt76_put_txwi(mdev, t);
+}
+
+static void
+mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len)
+{
+ __le32 *tx_free = (__le32 *)data, *cur_info;
+ struct mt76_dev *mdev = &dev->mt76;
+ struct mt76_phy *phy2 = mdev->phys[MT_BAND1];
+ struct mt76_phy *phy3 = mdev->phys[MT_BAND2];
+ struct mt76_txwi_cache *txwi;
+ struct ieee80211_sta *sta = NULL;
+ LIST_HEAD(free_list);
+ struct sk_buff *skb, *tmp;
+ void *end = data + len;
+ bool wake = false;
+ u16 total, count = 0;
+
+ /* clean DMA queues and unmap buffers first */
+ mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[MT_TXQ_PSD], false);
+ mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[MT_TXQ_BE], false);
+ if (phy2) {
+ mt76_queue_tx_cleanup(dev, phy2->q_tx[MT_TXQ_PSD], false);
+ mt76_queue_tx_cleanup(dev, phy2->q_tx[MT_TXQ_BE], false);
+ }
+ if (phy3) {
+ mt76_queue_tx_cleanup(dev, phy3->q_tx[MT_TXQ_PSD], false);
+ 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) < 4))
+ return;
+
+ total = le32_get_bits(tx_free[0], MT_TXFREE0_MSDU_CNT);
+ for (cur_info = &tx_free[2]; count < total; cur_info++) {
+ u32 msdu, info;
+ u8 i;
+
+ if (WARN_ON_ONCE((void *)cur_info >= end))
+ return;
+ /* 1'b1: new wcid pair.
+ * 1'b0: msdu_id with the same 'wcid pair' as above.
+ */
+ info = le32_to_cpu(*cur_info);
+ if (info & MT_TXFREE_INFO_PAIR) {
+ struct mt7996_sta *msta;
+ struct mt76_wcid *wcid;
+ 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);
+ spin_lock_bh(&dev->sta_poll_lock);
+ if (list_empty(&msta->poll_list))
+ list_add_tail(&msta->poll_list, &dev->sta_poll_list);
+ spin_unlock_bh(&dev->sta_poll_lock);
+ continue;
+ }
+
+ if (info & MT_TXFREE_INFO_HEADER)
+ continue;
+
+ for (i = 0; i < 2; i++) {
+ msdu = (info >> (15 * i)) & MT_TXFREE_INFO_MSDU_ID;
+ if (msdu == MT_TXFREE_INFO_MSDU_ID)
+ continue;
+
+ count++;
+ txwi = mt76_token_release(mdev, msdu, &wake);
+ if (!txwi)
+ continue;
+
+ mt7996_txwi_free(dev, txwi, sta, &free_list);
+ }
+ }
+
+ mt7996_mac_sta_poll(dev);
+
+ if (wake)
+ mt76_set_tx_blocked(&dev->mt76, false);
+
+ mt76_worker_schedule(&dev->mt76.tx_worker);
+
+ list_for_each_entry_safe(skb, tmp, &free_list, list) {
+ skb_list_del_init(skb);
+ napi_consume_skb(skb, 1);
+ }
+}
+
+static bool
+mt7996_mac_add_txs_skb(struct mt7996_dev *dev, struct mt76_wcid *wcid, int pid,
+ __le32 *txs_data, struct mt76_sta_stats *stats)
+{
+ struct ieee80211_supported_band *sband;
+ struct mt76_dev *mdev = &dev->mt76;
+ struct mt76_phy *mphy;
+ struct ieee80211_tx_info *info;
+ struct sk_buff_head list;
+ struct rate_info rate = {};
+ struct sk_buff *skb;
+ bool cck = false;
+ u32 txrate, txs, mode, stbc;
+
+ mt76_tx_status_lock(mdev, &list);
+ skb = mt76_tx_status_skb_get(mdev, wcid, pid, &list);
+ if (!skb)
+ goto out_no_skb;
+
+ txs = le32_to_cpu(txs_data[0]);
+
+ info = IEEE80211_SKB_CB(skb);
+ if (!(txs & MT_TXS0_ACK_ERROR_MASK))
+ info->flags |= IEEE80211_TX_STAT_ACK;
+
+ info->status.ampdu_len = 1;
+ info->status.ampdu_ack_len = !!(info->flags &
+ IEEE80211_TX_STAT_ACK);
+
+ info->status.rates[0].idx = -1;
+
+ txrate = FIELD_GET(MT_TXS0_TX_RATE, txs);
+
+ rate.mcs = FIELD_GET(MT_TX_RATE_IDX, txrate);
+ rate.nss = FIELD_GET(MT_TX_RATE_NSS, txrate) + 1;
+ stbc = le32_get_bits(txs_data[3], MT_TXS3_RATE_STBC);
+
+ if (stbc && rate.nss > 1)
+ rate.nss >>= 1;
+
+ if (rate.nss - 1 < ARRAY_SIZE(stats->tx_nss))
+ stats->tx_nss[rate.nss - 1]++;
+ if (rate.mcs < ARRAY_SIZE(stats->tx_mcs))
+ stats->tx_mcs[rate.mcs]++;
+
+ mode = FIELD_GET(MT_TX_RATE_MODE, txrate);
+ switch (mode) {
+ case MT_PHY_TYPE_CCK:
+ cck = true;
+ fallthrough;
+ case MT_PHY_TYPE_OFDM:
+ mphy = mt76_dev_phy(mdev, wcid->phy_idx);
+
+ if (mphy->chandef.chan->band == NL80211_BAND_5GHZ)
+ sband = &mphy->sband_5g.sband;
+ else if (mphy->chandef.chan->band == NL80211_BAND_6GHZ)
+ sband = &mphy->sband_6g.sband;
+ else
+ sband = &mphy->sband_2g.sband;
+
+ rate.mcs = mt76_get_rate(mphy->dev, sband, rate.mcs, cck);
+ rate.legacy = sband->bitrates[rate.mcs].bitrate;
+ break;
+ case MT_PHY_TYPE_HT:
+ case MT_PHY_TYPE_HT_GF:
+ if (rate.mcs > 31)
+ goto out;
+
+ rate.flags = RATE_INFO_FLAGS_MCS;
+ if (wcid->rate.flags & RATE_INFO_FLAGS_SHORT_GI)
+ rate.flags |= RATE_INFO_FLAGS_SHORT_GI;
+ break;
+ case MT_PHY_TYPE_VHT:
+ if (rate.mcs > 9)
+ goto out;
+
+ rate.flags = RATE_INFO_FLAGS_VHT_MCS;
+ break;
+ case MT_PHY_TYPE_HE_SU:
+ case MT_PHY_TYPE_HE_EXT_SU:
+ case MT_PHY_TYPE_HE_TB:
+ case MT_PHY_TYPE_HE_MU:
+ if (rate.mcs > 11)
+ goto out;
+
+ rate.he_gi = wcid->rate.he_gi;
+ rate.he_dcm = FIELD_GET(MT_TX_RATE_DCM, txrate);
+ rate.flags = RATE_INFO_FLAGS_HE_MCS;
+ break;
+ default:
+ goto out;
+ }
+
+ stats->tx_mode[mode]++;
+
+ switch (FIELD_GET(MT_TXS0_BW, txs)) {
+ case IEEE80211_STA_RX_BW_160:
+ rate.bw = RATE_INFO_BW_160;
+ stats->tx_bw[3]++;
+ break;
+ case IEEE80211_STA_RX_BW_80:
+ rate.bw = RATE_INFO_BW_80;
+ stats->tx_bw[2]++;
+ break;
+ case IEEE80211_STA_RX_BW_40:
+ rate.bw = RATE_INFO_BW_40;
+ stats->tx_bw[1]++;
+ break;
+ default:
+ rate.bw = RATE_INFO_BW_20;
+ stats->tx_bw[0]++;
+ break;
+ }
+ wcid->rate = rate;
+
+out:
+ mt76_tx_status_skb_done(mdev, skb, &list);
+
+out_no_skb:
+ mt76_tx_status_unlock(mdev, &list);
+
+ return !!skb;
+}
+
+static void mt7996_mac_add_txs(struct mt7996_dev *dev, void *data)
+{
+ struct mt7996_sta *msta = NULL;
+ struct mt76_wcid *wcid;
+ __le32 *txs_data = data;
+ u16 wcidx;
+ u8 pid;
+
+ if (le32_get_bits(txs_data[0], MT_TXS0_TXS_FORMAT) > 1)
+ return;
+
+ wcidx = le32_get_bits(txs_data[2], MT_TXS2_WCID);
+ pid = le32_get_bits(txs_data[3], MT_TXS3_PID);
+
+ if (pid < MT_PACKET_ID_FIRST)
+ return;
+
+ if (wcidx >= MT7996_WTBL_SIZE)
+ return;
+
+ rcu_read_lock();
+
+ wcid = rcu_dereference(dev->mt76.wcid[wcidx]);
+ if (!wcid)
+ goto out;
+
+ msta = container_of(wcid, struct mt7996_sta, wcid);
+
+ mt7996_mac_add_txs_skb(dev, wcid, pid, txs_data, &msta->stats);
+
+ if (!wcid->sta)
+ goto out;
+
+ spin_lock_bh(&dev->sta_poll_lock);
+ if (list_empty(&msta->poll_list))
+ list_add_tail(&msta->poll_list, &dev->sta_poll_list);
+ spin_unlock_bh(&dev->sta_poll_lock);
+
+out:
+ rcu_read_unlock();
+}
+
+bool mt7996_rx_check(struct mt76_dev *mdev, void *data, int len)
+{
+ struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
+ __le32 *rxd = (__le32 *)data;
+ __le32 *end = (__le32 *)&rxd[len / 4];
+ enum rx_pkt_type type;
+
+ type = le32_get_bits(rxd[0], MT_RXD0_PKT_TYPE);
+ if (type != PKT_TYPE_NORMAL) {
+ u32 sw_type = le32_get_bits(rxd[0], MT_RXD0_SW_PKT_TYPE_MASK);
+
+ if (unlikely((sw_type & MT_RXD0_SW_PKT_TYPE_MAP) ==
+ MT_RXD0_SW_PKT_TYPE_FRAME))
+ return true;
+ }
+
+ switch (type) {
+ case PKT_TYPE_TXRX_NOTIFY:
+ mt7996_mac_tx_free(dev, data, len);
+ return false;
+ case PKT_TYPE_TXS:
+ for (rxd += 4; rxd + 8 <= end; rxd += 8)
+ mt7996_mac_add_txs(dev, rxd);
+ return false;
+ case PKT_TYPE_RX_FW_MONITOR:
+ mt7996_debugfs_rx_fw_monitor(dev, data, len);
+ return false;
+ default:
+ return true;
+ }
+}
+
+void mt7996_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
+ struct sk_buff *skb, u32 *info)
+{
+ struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
+ __le32 *rxd = (__le32 *)skb->data;
+ __le32 *end = (__le32 *)&skb->data[skb->len];
+ enum rx_pkt_type type;
+
+ type = le32_get_bits(rxd[0], MT_RXD0_PKT_TYPE);
+ if (type != PKT_TYPE_NORMAL) {
+ u32 sw_type = le32_get_bits(rxd[0], MT_RXD0_SW_PKT_TYPE_MASK);
+
+ if (unlikely((sw_type & MT_RXD0_SW_PKT_TYPE_MAP) ==
+ MT_RXD0_SW_PKT_TYPE_FRAME))
+ type = PKT_TYPE_NORMAL;
+ }
+
+ switch (type) {
+ case PKT_TYPE_TXRX_NOTIFY:
+ mt7996_mac_tx_free(dev, skb->data, skb->len);
+ napi_consume_skb(skb, 1);
+ break;
+ case PKT_TYPE_RX_EVENT:
+ mt7996_mcu_rx_event(dev, skb);
+ break;
+ case PKT_TYPE_TXS:
+ for (rxd += 4; rxd + 8 <= end; rxd += 8)
+ mt7996_mac_add_txs(dev, rxd);
+ dev_kfree_skb(skb);
+ break;
+ case PKT_TYPE_RX_FW_MONITOR:
+ mt7996_debugfs_rx_fw_monitor(dev, skb->data, skb->len);
+ dev_kfree_skb(skb);
+ break;
+ case PKT_TYPE_NORMAL:
+ if (!mt7996_mac_fill_rx(dev, skb)) {
+ mt76_rx(&dev->mt76, q, skb);
+ return;
+ }
+ fallthrough;
+ default:
+ dev_kfree_skb(skb);
+ break;
+ }
+}
+
+void mt7996_tx_complete_skb(struct mt76_dev *mdev, struct mt76_queue_entry *e)
+{
+ if (!e->txwi) {
+ dev_kfree_skb_any(e->skb);
+ return;
+ }
+
+ /* error path */
+ if (e->skb == DMA_DUMMY_DATA) {
+ struct mt76_txwi_cache *t;
+ struct mt7996_txp *txp;
+
+ txp = mt7996_txwi_to_txp(mdev, e->txwi);
+ t = mt76_token_put(mdev, le16_to_cpu(txp->token));
+ e->skb = t ? t->skb : NULL;
+ }
+
+ if (e->skb)
+ mt76_tx_complete_skb(mdev, e->wcid, e->skb);
+}
+
+void mt7996_mac_cca_stats_reset(struct mt7996_phy *phy)
+{
+ struct mt7996_dev *dev = phy->dev;
+ u32 reg = MT_WF_PHYRX_BAND_RX_CTRL1(phy->mt76->band_idx);
+
+ mt76_clear(dev, reg, MT_WF_PHYRX_BAND_RX_CTRL1_STSCNT_EN);
+ mt76_set(dev, reg, BIT(11) | BIT(9));
+}
+
+void mt7996_mac_reset_counters(struct mt7996_phy *phy)
+{
+ struct mt7996_dev *dev = phy->dev;
+ u8 band_idx = phy->mt76->band_idx;
+ int i;
+
+ for (i = 0; i < 16; i++)
+ mt76_rr(dev, MT_TX_AGG_CNT(band_idx, i));
+
+ phy->mt76->survey_time = ktime_get_boottime();
+
+ memset(phy->mt76->aggr_stats, 0, sizeof(phy->mt76->aggr_stats));
+
+ /* reset airtime counters */
+ mt76_set(dev, MT_WF_RMAC_MIB_AIRTIME0(band_idx),
+ MT_WF_RMAC_MIB_RXTIME_CLR);
+
+ mt7996_mcu_get_chan_mib_info(phy, true);
+}
+
+void mt7996_mac_set_timing(struct mt7996_phy *phy)
+{
+ s16 coverage_class = phy->coverage_class;
+ struct mt7996_dev *dev = phy->dev;
+ struct mt7996_phy *phy2 = mt7996_phy2(dev);
+ struct mt7996_phy *phy3 = mt7996_phy3(dev);
+ u32 val, reg_offset;
+ u32 cck = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, 231) |
+ FIELD_PREP(MT_TIMEOUT_VAL_CCA, 48);
+ u32 ofdm = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, 60) |
+ FIELD_PREP(MT_TIMEOUT_VAL_CCA, 28);
+ u8 band_idx = phy->mt76->band_idx;
+ int offset;
+ bool a_band = !(phy->mt76->chandef.chan->band == NL80211_BAND_2GHZ);
+
+ if (!test_bit(MT76_STATE_RUNNING, &phy->mt76->state))
+ return;
+
+ if (phy2)
+ coverage_class = max_t(s16, dev->phy.coverage_class,
+ phy2->coverage_class);
+
+ if (phy3)
+ coverage_class = max_t(s16, coverage_class,
+ phy3->coverage_class);
+
+ mt76_set(dev, MT_ARB_SCR(band_idx),
+ MT_ARB_SCR_TX_DISABLE | MT_ARB_SCR_RX_DISABLE);
+ udelay(1);
+
+ offset = 3 * coverage_class;
+ reg_offset = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, offset) |
+ FIELD_PREP(MT_TIMEOUT_VAL_CCA, offset);
+
+ mt76_wr(dev, MT_TMAC_CDTR(band_idx), cck + reg_offset);
+ mt76_wr(dev, MT_TMAC_ODTR(band_idx), ofdm + reg_offset);
+ mt76_wr(dev, MT_TMAC_ICR0(band_idx),
+ FIELD_PREP(MT_IFS_EIFS_OFDM, a_band ? 84 : 78) |
+ FIELD_PREP(MT_IFS_RIFS, 2) |
+ FIELD_PREP(MT_IFS_SIFS, 10) |
+ FIELD_PREP(MT_IFS_SLOT, phy->slottime));
+
+ if (!a_band)
+ mt76_wr(dev, MT_TMAC_ICR1(band_idx),
+ FIELD_PREP(MT_IFS_EIFS_CCK, 314));
+
+ if (phy->slottime < 20 || a_band)
+ val = MT7996_CFEND_RATE_DEFAULT;
+ else
+ val = MT7996_CFEND_RATE_11B;
+
+ mt76_rmw_field(dev, MT_AGG_ACR0(band_idx), MT_AGG_ACR_CFEND_RATE, val);
+ mt76_clear(dev, MT_ARB_SCR(band_idx),
+ MT_ARB_SCR_TX_DISABLE | MT_ARB_SCR_RX_DISABLE);
+}
+
+void mt7996_mac_enable_nf(struct mt7996_dev *dev, u8 band)
+{
+ mt76_set(dev, MT_WF_PHYRX_CSD_BAND_RXTD12(band),
+ MT_WF_PHYRX_CSD_BAND_RXTD12_IRPI_SW_CLR_ONLY |
+ MT_WF_PHYRX_CSD_BAND_RXTD12_IRPI_SW_CLR);
+
+ mt76_set(dev, MT_WF_PHYRX_BAND_RX_CTRL1(band),
+ FIELD_PREP(MT_WF_PHYRX_BAND_RX_CTRL1_IPI_EN, 0x5));
+}
+
+static u8
+mt7996_phy_get_nf(struct mt7996_phy *phy, u8 band_idx)
+{
+ static const u8 nf_power[] = { 92, 89, 86, 83, 80, 75, 70, 65, 60, 55, 52 };
+ struct mt7996_dev *dev = phy->dev;
+ u32 val, sum = 0, n = 0;
+ int ant, i;
+
+ for (ant = 0; ant < hweight8(phy->mt76->antenna_mask); ant++) {
+ u32 reg = MT_WF_PHYRX_CSD_IRPI(band_idx, ant);
+
+ for (i = 0; i < ARRAY_SIZE(nf_power); i++, reg += 4) {
+ val = mt76_rr(dev, reg);
+ sum += val * nf_power[i];
+ n += val;
+ }
+ }
+
+ return n ? sum / n : 0;
+}
+
+void mt7996_update_channel(struct mt76_phy *mphy)
+{
+ struct mt7996_phy *phy = (struct mt7996_phy *)mphy->priv;
+ struct mt76_channel_state *state = mphy->chan_state;
+ int nf;
+
+ mt7996_mcu_get_chan_mib_info(phy, false);
+
+ nf = mt7996_phy_get_nf(phy, mphy->band_idx);
+ if (!phy->noise)
+ phy->noise = nf << 4;
+ else if (nf)
+ phy->noise += nf - (phy->noise >> 4);
+
+ state->noise = -(phy->noise >> 4);
+}
+
+static bool
+mt7996_wait_reset_state(struct mt7996_dev *dev, u32 state)
+{
+ bool ret;
+
+ ret = wait_event_timeout(dev->reset_wait,
+ (READ_ONCE(dev->reset_state) & state),
+ MT7996_RESET_TIMEOUT);
+
+ WARN(!ret, "Timeout waiting for MCU reset state %x\n", state);
+ return ret;
+}
+
+static void
+mt7996_update_vif_beacon(void *priv, u8 *mac, struct ieee80211_vif *vif)
+{
+ struct ieee80211_hw *hw = priv;
+
+ switch (vif->type) {
+ case NL80211_IFTYPE_MESH_POINT:
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_AP:
+ mt7996_mcu_add_beacon(hw, vif, vif->bss_conf.enable_beacon);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+mt7996_update_beacons(struct mt7996_dev *dev)
+{
+ struct mt76_phy *phy2, *phy3;
+
+ ieee80211_iterate_active_interfaces(dev->mt76.hw,
+ IEEE80211_IFACE_ITER_RESUME_ALL,
+ mt7996_update_vif_beacon, dev->mt76.hw);
+
+ phy2 = dev->mt76.phys[MT_BAND1];
+ if (!phy2)
+ return;
+
+ ieee80211_iterate_active_interfaces(phy2->hw,
+ IEEE80211_IFACE_ITER_RESUME_ALL,
+ mt7996_update_vif_beacon, phy2->hw);
+
+ phy3 = dev->mt76.phys[MT_BAND2];
+ if (!phy3)
+ return;
+
+ ieee80211_iterate_active_interfaces(phy3->hw,
+ IEEE80211_IFACE_ITER_RESUME_ALL,
+ mt7996_update_vif_beacon, phy3->hw);
+}
+
+static void
+mt7996_dma_reset(struct mt7996_dev *dev)
+{
+ struct mt76_phy *phy2 = dev->mt76.phys[MT_BAND1];
+ struct mt76_phy *phy3 = dev->mt76.phys[MT_BAND2];
+ u32 hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0);
+ int i;
+
+ mt76_clear(dev, MT_WFDMA0_GLO_CFG,
+ MT_WFDMA0_GLO_CFG_TX_DMA_EN |
+ MT_WFDMA0_GLO_CFG_RX_DMA_EN);
+
+ if (dev->hif2)
+ mt76_clear(dev, MT_WFDMA0_GLO_CFG + hif1_ofs,
+ MT_WFDMA0_GLO_CFG_TX_DMA_EN |
+ MT_WFDMA0_GLO_CFG_RX_DMA_EN);
+
+ usleep_range(1000, 2000);
+
+ for (i = 0; i < __MT_TXQ_MAX; i++) {
+ mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[i], true);
+ if (phy2)
+ mt76_queue_tx_cleanup(dev, phy2->q_tx[i], true);
+ if (phy3)
+ mt76_queue_tx_cleanup(dev, phy3->q_tx[i], true);
+ }
+
+ for (i = 0; i < __MT_MCUQ_MAX; i++)
+ mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[i], true);
+
+ mt76_for_each_q_rx(&dev->mt76, i)
+ mt76_queue_rx_reset(dev, i);
+
+ mt76_tx_status_check(&dev->mt76, true);
+
+ /* re-init prefetch settings after reset */
+ mt7996_dma_prefetch(dev);
+
+ mt76_set(dev, MT_WFDMA0_GLO_CFG,
+ MT_WFDMA0_GLO_CFG_TX_DMA_EN | MT_WFDMA0_GLO_CFG_RX_DMA_EN);
+
+ if (dev->hif2)
+ mt76_set(dev, MT_WFDMA0_GLO_CFG + hif1_ofs,
+ MT_WFDMA0_GLO_CFG_TX_DMA_EN |
+ MT_WFDMA0_GLO_CFG_RX_DMA_EN);
+}
+
+void mt7996_tx_token_put(struct mt7996_dev *dev)
+{
+ struct mt76_txwi_cache *txwi;
+ int id;
+
+ spin_lock_bh(&dev->mt76.token_lock);
+ idr_for_each_entry(&dev->mt76.token, txwi, id) {
+ mt7996_txwi_free(dev, txwi, NULL, NULL);
+ dev->mt76.token_count--;
+ }
+ spin_unlock_bh(&dev->mt76.token_lock);
+ idr_destroy(&dev->mt76.token);
+}
+
+/* system error recovery */
+void mt7996_mac_reset_work(struct work_struct *work)
+{
+ struct mt7996_phy *phy2, *phy3;
+ struct mt7996_dev *dev;
+ int i;
+
+ dev = container_of(work, struct mt7996_dev, reset_work);
+ phy2 = mt7996_phy2(dev);
+ phy3 = mt7996_phy3(dev);
+
+ if (!(READ_ONCE(dev->reset_state) & MT_MCU_CMD_STOP_DMA))
+ return;
+
+ ieee80211_stop_queues(mt76_hw(dev));
+ if (phy2)
+ ieee80211_stop_queues(phy2->mt76->hw);
+ if (phy3)
+ ieee80211_stop_queues(phy3->mt76->hw);
+
+ set_bit(MT76_RESET, &dev->mphy.state);
+ set_bit(MT76_MCU_RESET, &dev->mphy.state);
+ wake_up(&dev->mt76.mcu.wait);
+ cancel_delayed_work_sync(&dev->mphy.mac_work);
+ if (phy2) {
+ set_bit(MT76_RESET, &phy2->mt76->state);
+ cancel_delayed_work_sync(&phy2->mt76->mac_work);
+ }
+ if (phy3) {
+ set_bit(MT76_RESET, &phy3->mt76->state);
+ cancel_delayed_work_sync(&phy3->mt76->mac_work);
+ }
+ mt76_worker_disable(&dev->mt76.tx_worker);
+ mt76_for_each_q_rx(&dev->mt76, i)
+ napi_disable(&dev->mt76.napi[i]);
+ napi_disable(&dev->mt76.tx_napi);
+
+ mutex_lock(&dev->mt76.mutex);
+
+ mt76_wr(dev, MT_MCU_INT_EVENT, MT_MCU_INT_EVENT_DMA_STOPPED);
+
+ if (mt7996_wait_reset_state(dev, MT_MCU_CMD_RESET_DONE)) {
+ mt7996_dma_reset(dev);
+
+ mt7996_tx_token_put(dev);
+ idr_init(&dev->mt76.token);
+
+ mt76_wr(dev, MT_MCU_INT_EVENT, MT_MCU_INT_EVENT_DMA_INIT);
+ mt7996_wait_reset_state(dev, MT_MCU_CMD_RECOVERY_DONE);
+ }
+
+ clear_bit(MT76_MCU_RESET, &dev->mphy.state);
+ clear_bit(MT76_RESET, &dev->mphy.state);
+ if (phy2)
+ clear_bit(MT76_RESET, &phy2->mt76->state);
+ if (phy3)
+ clear_bit(MT76_RESET, &phy3->mt76->state);
+
+ local_bh_disable();
+ mt76_for_each_q_rx(&dev->mt76, i) {
+ napi_enable(&dev->mt76.napi[i]);
+ napi_schedule(&dev->mt76.napi[i]);
+ }
+ local_bh_enable();
+
+ tasklet_schedule(&dev->irq_tasklet);
+
+ mt76_wr(dev, MT_MCU_INT_EVENT, MT_MCU_INT_EVENT_RESET_DONE);
+ mt7996_wait_reset_state(dev, MT_MCU_CMD_NORMAL_STATE);
+
+ mt76_worker_enable(&dev->mt76.tx_worker);
+
+ local_bh_disable();
+ napi_enable(&dev->mt76.tx_napi);
+ napi_schedule(&dev->mt76.tx_napi);
+ local_bh_enable();
+
+ ieee80211_wake_queues(mt76_hw(dev));
+ if (phy2)
+ ieee80211_wake_queues(phy2->mt76->hw);
+ if (phy3)
+ ieee80211_wake_queues(phy3->mt76->hw);
+
+ mutex_unlock(&dev->mt76.mutex);
+
+ mt7996_update_beacons(dev);
+
+ ieee80211_queue_delayed_work(mt76_hw(dev), &dev->mphy.mac_work,
+ MT7996_WATCHDOG_TIME);
+ if (phy2)
+ ieee80211_queue_delayed_work(phy2->mt76->hw,
+ &phy2->mt76->mac_work,
+ MT7996_WATCHDOG_TIME);
+ if (phy3)
+ ieee80211_queue_delayed_work(phy3->mt76->hw,
+ &phy3->mt76->mac_work,
+ MT7996_WATCHDOG_TIME);
+}
+
+void mt7996_mac_update_stats(struct mt7996_phy *phy)
+{
+ struct mt7996_dev *dev = phy->dev;
+ struct mib_stats *mib = &phy->mib;
+ u8 band_idx = phy->mt76->band_idx;
+ u32 cnt;
+ int i;
+
+ cnt = mt76_rr(dev, MT_MIB_RSCR1(band_idx));
+ mib->fcs_err_cnt += cnt;
+
+ cnt = mt76_rr(dev, MT_MIB_RSCR33(band_idx));
+ mib->rx_fifo_full_cnt += cnt;
+
+ cnt = mt76_rr(dev, MT_MIB_RSCR31(band_idx));
+ mib->rx_mpdu_cnt += cnt;
+
+ cnt = mt76_rr(dev, MT_MIB_SDR6(band_idx));
+ mib->channel_idle_cnt += FIELD_GET(MT_MIB_SDR6_CHANNEL_IDL_CNT_MASK, cnt);
+
+ cnt = mt76_rr(dev, MT_MIB_RVSR0(band_idx));
+ mib->rx_vector_mismatch_cnt += cnt;
+
+ cnt = mt76_rr(dev, MT_MIB_RSCR35(band_idx));
+ mib->rx_delimiter_fail_cnt += cnt;
+
+ cnt = mt76_rr(dev, MT_MIB_RSCR36(band_idx));
+ mib->rx_len_mismatch_cnt += cnt;
+
+ cnt = mt76_rr(dev, MT_MIB_TSCR0(band_idx));
+ mib->tx_ampdu_cnt += cnt;
+
+ cnt = mt76_rr(dev, MT_MIB_TSCR2(band_idx));
+ mib->tx_stop_q_empty_cnt += cnt;
+
+ cnt = mt76_rr(dev, MT_MIB_TSCR3(band_idx));
+ mib->tx_mpdu_attempts_cnt += cnt;
+
+ cnt = mt76_rr(dev, MT_MIB_TSCR4(band_idx));
+ mib->tx_mpdu_success_cnt += cnt;
+
+ cnt = mt76_rr(dev, MT_MIB_RSCR27(band_idx));
+ mib->rx_ampdu_cnt += cnt;
+
+ cnt = mt76_rr(dev, MT_MIB_RSCR28(band_idx));
+ mib->rx_ampdu_bytes_cnt += cnt;
+
+ cnt = mt76_rr(dev, MT_MIB_RSCR29(band_idx));
+ mib->rx_ampdu_valid_subframe_cnt += cnt;
+
+ cnt = mt76_rr(dev, MT_MIB_RSCR30(band_idx));
+ mib->rx_ampdu_valid_subframe_bytes_cnt += cnt;
+
+ cnt = mt76_rr(dev, MT_MIB_SDR27(band_idx));
+ mib->tx_rwp_fail_cnt += FIELD_GET(MT_MIB_SDR27_TX_RWP_FAIL_CNT, cnt);
+
+ cnt = mt76_rr(dev, MT_MIB_SDR28(band_idx));
+ mib->tx_rwp_need_cnt += FIELD_GET(MT_MIB_SDR28_TX_RWP_NEED_CNT, cnt);
+
+ cnt = mt76_rr(dev, MT_UMIB_RPDCR(band_idx));
+ mib->rx_pfdrop_cnt += cnt;
+
+ cnt = mt76_rr(dev, MT_MIB_RVSR1(band_idx));
+ mib->rx_vec_queue_overflow_drop_cnt += cnt;
+
+ cnt = mt76_rr(dev, MT_MIB_TSCR1(band_idx));
+ mib->rx_ba_cnt += cnt;
+
+ cnt = mt76_rr(dev, MT_MIB_BSCR0(band_idx));
+ mib->tx_bf_ebf_ppdu_cnt += cnt;
+
+ cnt = mt76_rr(dev, MT_MIB_BSCR1(band_idx));
+ mib->tx_bf_ibf_ppdu_cnt += cnt;
+
+ cnt = mt76_rr(dev, MT_MIB_BSCR2(band_idx));
+ mib->tx_mu_bf_cnt += cnt;
+
+ cnt = mt76_rr(dev, MT_MIB_TSCR5(band_idx));
+ mib->tx_mu_mpdu_cnt += cnt;
+
+ cnt = mt76_rr(dev, MT_MIB_TSCR6(band_idx));
+ mib->tx_mu_acked_mpdu_cnt += cnt;
+
+ cnt = mt76_rr(dev, MT_MIB_TSCR7(band_idx));
+ mib->tx_su_acked_mpdu_cnt += cnt;
+
+ cnt = mt76_rr(dev, MT_MIB_BSCR3(band_idx));
+ mib->tx_bf_rx_fb_ht_cnt += cnt;
+ mib->tx_bf_rx_fb_all_cnt += cnt;
+
+ cnt = mt76_rr(dev, MT_MIB_BSCR4(band_idx));
+ mib->tx_bf_rx_fb_vht_cnt += cnt;
+ mib->tx_bf_rx_fb_all_cnt += cnt;
+
+ cnt = mt76_rr(dev, MT_MIB_BSCR5(band_idx));
+ mib->tx_bf_rx_fb_he_cnt += cnt;
+ mib->tx_bf_rx_fb_all_cnt += cnt;
+
+ cnt = mt76_rr(dev, MT_MIB_BSCR6(band_idx));
+ mib->tx_bf_rx_fb_eht_cnt += cnt;
+ mib->tx_bf_rx_fb_all_cnt += cnt;
+
+ cnt = mt76_rr(dev, MT_ETBF_RX_FB_CONT(band_idx));
+ mib->tx_bf_rx_fb_bw = FIELD_GET(MT_ETBF_RX_FB_BW, cnt);
+ mib->tx_bf_rx_fb_nc_cnt += FIELD_GET(MT_ETBF_RX_FB_NC, cnt);
+ mib->tx_bf_rx_fb_nr_cnt += FIELD_GET(MT_ETBF_RX_FB_NR, cnt);
+
+ cnt = mt76_rr(dev, MT_MIB_BSCR7(band_idx));
+ mib->tx_bf_fb_trig_cnt += cnt;
+
+ cnt = mt76_rr(dev, MT_MIB_BSCR17(band_idx));
+ mib->tx_bf_fb_cpl_cnt += cnt;
+
+ for (i = 0; i < ARRAY_SIZE(mib->tx_amsdu); i++) {
+ cnt = mt76_rr(dev, MT_PLE_AMSDU_PACK_MSDU_CNT(i));
+ mib->tx_amsdu[i] += cnt;
+ mib->tx_amsdu_cnt += cnt;
+ }
+
+ /* rts count */
+ cnt = mt76_rr(dev, MT_MIB_BTSCR5(band_idx));
+ mib->rts_cnt += cnt;
+
+ /* rts retry count */
+ cnt = mt76_rr(dev, MT_MIB_BTSCR6(band_idx));
+ mib->rts_retries_cnt += cnt;
+
+ /* ba miss count */
+ cnt = mt76_rr(dev, MT_MIB_BTSCR0(band_idx));
+ mib->ba_miss_cnt += cnt;
+
+ /* ack fail count */
+ cnt = mt76_rr(dev, MT_MIB_BFTFCR(band_idx));
+ mib->ack_fail_cnt += cnt;
+
+ for (i = 0; i < 16; i++) {
+ cnt = mt76_rr(dev, MT_TX_AGG_CNT(band_idx, i));
+ phy->mt76->aggr_stats[i] += cnt;
+ }
+}
+
+void mt7996_mac_sta_rc_work(struct work_struct *work)
+{
+ struct mt7996_dev *dev = container_of(work, struct mt7996_dev, rc_work);
+ struct ieee80211_sta *sta;
+ struct ieee80211_vif *vif;
+ struct mt7996_sta *msta;
+ u32 changed;
+ LIST_HEAD(list);
+
+ spin_lock_bh(&dev->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;
+ spin_unlock_bh(&dev->sta_poll_lock);
+
+ sta = container_of((void *)msta, struct ieee80211_sta, drv_priv);
+ vif = container_of((void *)msta->vif, struct ieee80211_vif, drv_priv);
+
+ if (changed & (IEEE80211_RC_SUPP_RATES_CHANGED |
+ IEEE80211_RC_NSS_CHANGED |
+ IEEE80211_RC_BW_CHANGED))
+ mt7996_mcu_add_rate_ctrl(dev, vif, sta, true);
+
+ /* TODO: smps change */
+
+ spin_lock_bh(&dev->sta_poll_lock);
+ }
+
+ spin_unlock_bh(&dev->sta_poll_lock);
+}
+
+void mt7996_mac_work(struct work_struct *work)
+{
+ struct mt7996_phy *phy;
+ struct mt76_phy *mphy;
+
+ mphy = (struct mt76_phy *)container_of(work, struct mt76_phy,
+ mac_work.work);
+ phy = mphy->priv;
+
+ mutex_lock(&mphy->dev->mutex);
+
+ mt76_update_survey(mphy);
+ if (++mphy->mac_work_count == 5) {
+ mphy->mac_work_count = 0;
+
+ mt7996_mac_update_stats(phy);
+ }
+
+ mutex_unlock(&mphy->dev->mutex);
+
+ mt76_tx_status_check(mphy->dev, false);
+
+ ieee80211_queue_delayed_work(mphy->hw, &mphy->mac_work,
+ MT7996_WATCHDOG_TIME);
+}
+
+static void mt7996_dfs_stop_radar_detector(struct mt7996_phy *phy)
+{
+ struct mt7996_dev *dev = phy->dev;
+
+ 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);
+}
+
+static int mt7996_dfs_start_rdd(struct mt7996_dev *dev, int chain)
+{
+ int err, region;
+
+ switch (dev->mt76.region) {
+ case NL80211_DFS_ETSI:
+ region = 0;
+ break;
+ case NL80211_DFS_JP:
+ region = 2;
+ break;
+ case NL80211_DFS_FCC:
+ default:
+ region = 1;
+ break;
+ }
+
+ err = mt7996_mcu_rdd_cmd(dev, RDD_START, chain,
+ MT_RX_SEL0, region);
+ if (err < 0)
+ return err;
+
+ return mt7996_mcu_rdd_cmd(dev, RDD_DET_MODE, chain,
+ MT_RX_SEL0, 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;
+
+ /* start CAC */
+ err = mt7996_mcu_rdd_cmd(dev, RDD_CAC_START, band_idx,
+ MT_RX_SEL0, 0);
+ if (err < 0)
+ return err;
+
+ err = mt7996_dfs_start_rdd(dev, band_idx);
+ if (err < 0)
+ return err;
+
+ phy->rdd_state |= BIT(band_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;
+}
+
+static int
+mt7996_dfs_init_radar_specs(struct mt7996_phy *phy)
+{
+ const struct mt7996_dfs_radar_spec *radar_specs;
+ struct mt7996_dev *dev = phy->dev;
+ int err, i;
+
+ switch (dev->mt76.region) {
+ case NL80211_DFS_FCC:
+ radar_specs = &fcc_radar_specs;
+ err = mt7996_mcu_set_fcc5_lpn(dev, 8);
+ if (err < 0)
+ return err;
+ break;
+ case NL80211_DFS_ETSI:
+ radar_specs = &etsi_radar_specs;
+ break;
+ case NL80211_DFS_JP:
+ radar_specs = &jp_radar_specs;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(radar_specs->radar_pattern); i++) {
+ err = mt7996_mcu_set_radar_th(dev, i,
+ &radar_specs->radar_pattern[i]);
+ if (err < 0)
+ return err;
+ }
+
+ return mt7996_mcu_set_pulse_th(dev, &radar_specs->pulse_th);
+}
+
+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;
+
+ prev_state = phy->mt76->dfs_state;
+ dfs_state = mt76_phy_dfs_state(phy->mt76);
+
+ if (prev_state == dfs_state)
+ return 0;
+
+ if (prev_state == MT_DFS_STATE_UNKNOWN)
+ mt7996_dfs_stop_radar_detector(phy);
+
+ if (dfs_state == MT_DFS_STATE_DISABLED)
+ goto stop;
+
+ if (prev_state <= MT_DFS_STATE_DISABLED) {
+ err = mt7996_dfs_init_radar_specs(phy);
+ if (err < 0)
+ return err;
+
+ err = mt7996_dfs_start_radar_detector(phy);
+ if (err < 0)
+ return err;
+
+ phy->mt76->dfs_state = MT_DFS_STATE_CAC;
+ }
+
+ 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);
+ if (err < 0) {
+ phy->mt76->dfs_state = MT_DFS_STATE_UNKNOWN;
+ return err;
+ }
+
+ phy->mt76->dfs_state = MT_DFS_STATE_ACTIVE;
+ return 0;
+
+stop:
+ err = mt7996_mcu_rdd_cmd(dev, RDD_NORMAL_START,
+ phy->mt76->band_idx, MT_RX_SEL0, 0);
+ if (err < 0)
+ return err;
+
+ mt7996_dfs_stop_radar_detector(phy);
+ phy->mt76->dfs_state = MT_DFS_STATE_DISABLED;
+
+ return 0;
+}
+
+static int
+mt7996_mac_twt_duration_align(int duration)
+{
+ return duration << 8;
+}
+
+static u64
+mt7996_mac_twt_sched_list_add(struct mt7996_dev *dev,
+ struct mt7996_twt_flow *flow)
+{
+ struct mt7996_twt_flow *iter, *iter_next;
+ u32 duration = flow->duration << 8;
+ u64 start_tsf;
+
+ iter = list_first_entry_or_null(&dev->twt_list,
+ struct mt7996_twt_flow, list);
+ if (!iter || !iter->sched || iter->start_tsf > duration) {
+ /* add flow as first entry in the list */
+ list_add(&flow->list, &dev->twt_list);
+ return 0;
+ }
+
+ list_for_each_entry_safe(iter, iter_next, &dev->twt_list, list) {
+ start_tsf = iter->start_tsf +
+ mt7996_mac_twt_duration_align(iter->duration);
+ if (list_is_last(&iter->list, &dev->twt_list))
+ break;
+
+ if (!iter_next->sched ||
+ iter_next->start_tsf > start_tsf + duration) {
+ list_add(&flow->list, &iter->list);
+ goto out;
+ }
+ }
+
+ /* add flow as last entry in the list */
+ list_add_tail(&flow->list, &dev->twt_list);
+out:
+ return start_tsf;
+}
+
+static int mt7996_mac_check_twt_req(struct ieee80211_twt_setup *twt)
+{
+ struct ieee80211_twt_params *twt_agrt;
+ u64 interval, duration;
+ u16 mantissa;
+ u8 exp;
+
+ /* only individual agreement supported */
+ if (twt->control & IEEE80211_TWT_CONTROL_NEG_TYPE_BROADCAST)
+ return -EOPNOTSUPP;
+
+ /* only 256us unit supported */
+ if (twt->control & IEEE80211_TWT_CONTROL_WAKE_DUR_UNIT)
+ return -EOPNOTSUPP;
+
+ twt_agrt = (struct ieee80211_twt_params *)twt->params;
+
+ /* explicit agreement not supported */
+ if (!(twt_agrt->req_type & cpu_to_le16(IEEE80211_TWT_REQTYPE_IMPLICIT)))
+ return -EOPNOTSUPP;
+
+ exp = FIELD_GET(IEEE80211_TWT_REQTYPE_WAKE_INT_EXP,
+ le16_to_cpu(twt_agrt->req_type));
+ mantissa = le16_to_cpu(twt_agrt->mantissa);
+ duration = twt_agrt->min_twt_dur << 8;
+
+ interval = (u64)mantissa << exp;
+ if (interval < duration)
+ return -EOPNOTSUPP;
+
+ return 0;
+}
+
+void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw,
+ struct ieee80211_sta *sta,
+ struct ieee80211_twt_setup *twt)
+{
+ 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;
+ 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);
+ struct mt7996_twt_flow *flow;
+ int flowid, table_id;
+ u8 exp;
+
+ if (mt7996_mac_check_twt_req(twt))
+ goto out;
+
+ mutex_lock(&dev->mt76.mutex);
+
+ if (dev->twt.n_agrt == MT7996_MAX_TWT_AGRT)
+ goto unlock;
+
+ if (hweight8(msta->twt.flowid_mask) == ARRAY_SIZE(msta->twt.flow))
+ goto unlock;
+
+ flowid = ffs(~msta->twt.flowid_mask) - 1;
+ le16p_replace_bits(&twt_agrt->req_type, flowid,
+ IEEE80211_TWT_REQTYPE_FLOWID);
+
+ table_id = ffs(~dev->twt.table_mask) - 1;
+ 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];
+ memset(flow, 0, sizeof(*flow));
+ INIT_LIST_HEAD(&flow->list);
+ flow->wcid = msta->wcid.idx;
+ flow->table_id = table_id;
+ flow->id = flowid;
+ flow->duration = twt_agrt->min_twt_dur;
+ flow->mantissa = twt_agrt->mantissa;
+ flow->exp = exp;
+ flow->protection = !!(req_type & IEEE80211_TWT_REQTYPE_PROTECTION);
+ flow->flowtype = !!(req_type & IEEE80211_TWT_REQTYPE_FLOWTYPE);
+ flow->trigger = !!(req_type & IEEE80211_TWT_REQTYPE_TRIGGER);
+
+ if (sta_setup_cmd == TWT_SETUP_CMD_REQUEST ||
+ sta_setup_cmd == TWT_SETUP_CMD_SUGGEST) {
+ u64 interval = (u64)le16_to_cpu(twt_agrt->mantissa) << exp;
+ u64 flow_tsf, curr_tsf;
+ u32 rem;
+
+ flow->sched = true;
+ flow->start_tsf = mt7996_mac_twt_sched_list_add(dev, flow);
+ curr_tsf = __mt7996_get_tsf(hw, msta->vif);
+ div_u64_rem(curr_tsf - flow->start_tsf, interval, &rem);
+ flow_tsf = curr_tsf + interval - rem;
+ twt_agrt->twt = cpu_to_le64(flow_tsf);
+ } else {
+ list_add_tail(&flow->list, &dev->twt_list);
+ }
+ flow->tsf = le64_to_cpu(twt_agrt->twt);
+
+ if (mt7996_mcu_twt_agrt_update(dev, msta->vif, 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);
+ dev->twt.n_agrt++;
+
+unlock:
+ mutex_unlock(&dev->mt76.mutex);
+out:
+ le16p_replace_bits(&twt_agrt->req_type, setup_cmd,
+ IEEE80211_TWT_REQTYPE_SETUP_CMD);
+ twt->control = (twt->control & IEEE80211_TWT_CONTROL_WAKE_DUR_UNIT) |
+ (twt->control & IEEE80211_TWT_CONTROL_RX_DISABLED);
+}
+
+void mt7996_mac_twt_teardown_flow(struct mt7996_dev *dev,
+ struct mt7996_sta *msta,
+ u8 flowid)
+{
+ struct mt7996_twt_flow *flow;
+
+ lockdep_assert_held(&dev->mt76.mutex);
+
+ if (flowid >= ARRAY_SIZE(msta->twt.flow))
+ return;
+
+ if (!(msta->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))
+ return;
+
+ list_del_init(&flow->list);
+ msta->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/mac.h b/drivers/net/wireless/mediatek/mt76/mt7996/mac.h
new file mode 100644
index 000000000000..9f68852012b9
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.h
@@ -0,0 +1,398 @@
+/* SPDX-License-Identifier: ISC */
+/*
+ * Copyright (C) 2022 MediaTek Inc.
+ */
+
+#ifndef __MT7996_MAC_H
+#define __MT7996_MAC_H
+
+#define MT_CT_PARSE_LEN 72
+#define MT_CT_DMA_BUF_NUM 2
+
+#define MT_RXD0_LENGTH GENMASK(15, 0)
+#define MT_RXD0_PKT_TYPE GENMASK(31, 27)
+
+#define MT_RXD0_NORMAL_ETH_TYPE_OFS GENMASK(22, 16)
+#define MT_RXD0_NORMAL_IP_SUM BIT(23)
+#define MT_RXD0_NORMAL_UDP_TCP_SUM BIT(24)
+
+#define MT_RXD0_SW_PKT_TYPE_MASK GENMASK(31, 16)
+#define MT_RXD0_SW_PKT_TYPE_MAP 0x380F
+#define MT_RXD0_SW_PKT_TYPE_FRAME 0x3801
+
+enum rx_pkt_type {
+ PKT_TYPE_TXS,
+ PKT_TYPE_TXRXV,
+ PKT_TYPE_NORMAL,
+ PKT_TYPE_RX_DUP_RFB,
+ PKT_TYPE_RX_TMR,
+ PKT_TYPE_RETRIEVE,
+ PKT_TYPE_TXRX_NOTIFY,
+ PKT_TYPE_RX_EVENT,
+ PKT_TYPE_RX_FW_MONITOR = 0x0c,
+};
+
+/* RXD DW1 */
+#define MT_RXD1_NORMAL_WLAN_IDX GENMASK(11, 0)
+#define MT_RXD1_NORMAL_GROUP_1 BIT(16)
+#define MT_RXD1_NORMAL_GROUP_2 BIT(17)
+#define MT_RXD1_NORMAL_GROUP_3 BIT(18)
+#define MT_RXD1_NORMAL_GROUP_4 BIT(19)
+#define MT_RXD1_NORMAL_GROUP_5 BIT(20)
+#define MT_RXD1_NORMAL_KEY_ID GENMASK(22, 21)
+#define MT_RXD1_NORMAL_CM BIT(23)
+#define MT_RXD1_NORMAL_CLM BIT(24)
+#define MT_RXD1_NORMAL_ICV_ERR BIT(25)
+#define MT_RXD1_NORMAL_TKIP_MIC_ERR BIT(26)
+#define MT_RXD1_NORMAL_BAND_IDX GENMASK(28, 27)
+#define MT_RXD1_NORMAL_SPP_EN BIT(29)
+#define MT_RXD1_NORMAL_ADD_OM BIT(30)
+#define MT_RXD1_NORMAL_SEC_DONE BIT(31)
+
+/* RXD DW2 */
+#define MT_RXD2_NORMAL_BSSID GENMASK(5, 0)
+#define MT_RXD2_NORMAL_MAC_HDR_LEN GENMASK(12, 8)
+#define MT_RXD2_NORMAL_HDR_TRANS BIT(7)
+#define MT_RXD2_NORMAL_HDR_OFFSET GENMASK(15, 13)
+#define MT_RXD2_NORMAL_SEC_MODE GENMASK(20, 16)
+#define MT_RXD2_NORMAL_MU_BAR BIT(21)
+#define MT_RXD2_NORMAL_SW_BIT BIT(22)
+#define MT_RXD2_NORMAL_AMSDU_ERR BIT(23)
+#define MT_RXD2_NORMAL_MAX_LEN_ERROR BIT(24)
+#define MT_RXD2_NORMAL_HDR_TRANS_ERROR BIT(25)
+#define MT_RXD2_NORMAL_INT_FRAME BIT(26)
+#define MT_RXD2_NORMAL_FRAG BIT(27)
+#define MT_RXD2_NORMAL_NULL_FRAME BIT(28)
+#define MT_RXD2_NORMAL_NDATA BIT(29)
+#define MT_RXD2_NORMAL_NON_AMPDU BIT(30)
+#define MT_RXD2_NORMAL_BF_REPORT BIT(31)
+
+/* RXD DW3 */
+#define MT_RXD3_NORMAL_RXV_SEQ GENMASK(7, 0)
+#define MT_RXD3_NORMAL_CH_FREQ GENMASK(15, 8)
+#define MT_RXD3_NORMAL_ADDR_TYPE GENMASK(17, 16)
+#define MT_RXD3_NORMAL_U2M BIT(0)
+#define MT_RXD3_NORMAL_HTC_VLD BIT(18)
+#define MT_RXD3_NORMAL_BEACON_MC BIT(20)
+#define MT_RXD3_NORMAL_BEACON_UC BIT(21)
+#define MT_RXD3_NORMAL_CO_ANT BIT(22)
+#define MT_RXD3_NORMAL_FCS_ERR BIT(24)
+#define MT_RXD3_NORMAL_VLAN2ETH BIT(31)
+
+/* RXD DW4 */
+#define MT_RXD4_NORMAL_PAYLOAD_FORMAT GENMASK(1, 0)
+#define MT_RXD4_FIRST_AMSDU_FRAME GENMASK(1, 0)
+#define MT_RXD4_MID_AMSDU_FRAME BIT(1)
+#define MT_RXD4_LAST_AMSDU_FRAME BIT(0)
+
+#define MT_RXV_HDR_BAND_IDX BIT(24)
+
+/* RXD GROUP4 */
+#define MT_RXD8_FRAME_CONTROL GENMASK(15, 0)
+
+#define MT_RXD10_SEQ_CTRL GENMASK(15, 0)
+#define MT_RXD10_QOS_CTL GENMASK(31, 16)
+
+#define MT_RXD11_HT_CONTROL GENMASK(31, 0)
+
+/* P-RXV */
+#define MT_PRXV_TX_RATE GENMASK(6, 0)
+#define MT_PRXV_TX_DCM BIT(4)
+#define MT_PRXV_TX_ER_SU_106T BIT(5)
+#define MT_PRXV_NSTS GENMASK(10, 7)
+#define MT_PRXV_TXBF BIT(11)
+#define MT_PRXV_HT_AD_CODE BIT(12)
+#define MT_PRXV_HE_RU_ALLOC_L GENMASK(31, 28)
+#define MT_PRXV_HE_RU_ALLOC_H GENMASK(3, 0)
+#define MT_PRXV_RCPI3 GENMASK(31, 24)
+#define MT_PRXV_RCPI2 GENMASK(23, 16)
+#define MT_PRXV_RCPI1 GENMASK(15, 8)
+#define MT_PRXV_RCPI0 GENMASK(7, 0)
+#define MT_PRXV_HT_SHORT_GI GENMASK(4, 3)
+#define MT_PRXV_HT_STBC GENMASK(10, 9)
+#define MT_PRXV_TX_MODE GENMASK(14, 11)
+#define MT_PRXV_FRAME_MODE GENMASK(2, 0)
+#define MT_PRXV_DCM BIT(5)
+#define MT_PRXV_NUM_RX BIT(8, 6)
+
+/* C-RXV */
+#define MT_CRXV_HT_STBC GENMASK(1, 0)
+#define MT_CRXV_TX_MODE GENMASK(7, 4)
+#define MT_CRXV_FRAME_MODE GENMASK(10, 8)
+#define MT_CRXV_HT_SHORT_GI GENMASK(14, 13)
+#define MT_CRXV_HE_LTF_SIZE GENMASK(18, 17)
+#define MT_CRXV_HE_LDPC_EXT_SYM BIT(20)
+#define MT_CRXV_HE_PE_DISAMBIG BIT(23)
+#define MT_CRXV_HE_NUM_USER GENMASK(30, 24)
+#define MT_CRXV_HE_UPLINK BIT(31)
+#define MT_CRXV_HE_RU0 GENMASK(7, 0)
+#define MT_CRXV_HE_RU1 GENMASK(15, 8)
+#define MT_CRXV_HE_RU2 GENMASK(23, 16)
+#define MT_CRXV_HE_RU3 GENMASK(31, 24)
+
+#define MT_CRXV_HE_MU_AID GENMASK(30, 20)
+
+#define MT_CRXV_HE_SR_MASK GENMASK(11, 8)
+#define MT_CRXV_HE_SR1_MASK GENMASK(16, 12)
+#define MT_CRXV_HE_SR2_MASK GENMASK(20, 17)
+#define MT_CRXV_HE_SR3_MASK GENMASK(24, 21)
+
+#define MT_CRXV_HE_BSS_COLOR GENMASK(5, 0)
+#define MT_CRXV_HE_TXOP_DUR GENMASK(12, 6)
+#define MT_CRXV_HE_BEAM_CHNG BIT(13)
+#define MT_CRXV_HE_DOPPLER BIT(16)
+
+enum tx_header_format {
+ MT_HDR_FORMAT_802_3,
+ MT_HDR_FORMAT_CMD,
+ MT_HDR_FORMAT_802_11,
+ MT_HDR_FORMAT_802_11_EXT,
+};
+
+enum tx_pkt_type {
+ MT_TX_TYPE_CT,
+ MT_TX_TYPE_SF,
+ MT_TX_TYPE_CMD,
+ MT_TX_TYPE_FW,
+};
+
+enum tx_port_idx {
+ MT_TX_PORT_IDX_LMAC,
+ MT_TX_PORT_IDX_MCU
+};
+
+enum tx_mcu_port_q_idx {
+ MT_TX_MCU_PORT_RX_Q0 = 0x20,
+ MT_TX_MCU_PORT_RX_Q1,
+ MT_TX_MCU_PORT_RX_Q2,
+ MT_TX_MCU_PORT_RX_Q3,
+ MT_TX_MCU_PORT_RX_FWDL = 0x3e
+};
+
+enum tx_mgnt_type {
+ MT_TX_NORMAL,
+ MT_TX_TIMING,
+ MT_TX_ADDBA,
+};
+
+#define MT_CT_INFO_APPLY_TXD BIT(0)
+#define MT_CT_INFO_COPY_HOST_TXD_ALL BIT(1)
+#define MT_CT_INFO_MGMT_FRAME BIT(2)
+#define MT_CT_INFO_NONE_CIPHER_FRAME BIT(3)
+#define MT_CT_INFO_HSR2_TX BIT(4)
+#define MT_CT_INFO_FROM_HOST BIT(7)
+
+#define MT_TXD_SIZE (8 * 4)
+
+#define MT_TXD0_Q_IDX GENMASK(31, 25)
+#define MT_TXD0_PKT_FMT GENMASK(24, 23)
+#define MT_TXD0_ETH_TYPE_OFFSET GENMASK(22, 16)
+#define MT_TXD0_TX_BYTES GENMASK(15, 0)
+
+#define MT_TXD1_FIXED_RATE BIT(31)
+#define MT_TXD1_OWN_MAC GENMASK(30, 25)
+#define MT_TXD1_TID GENMASK(24, 21)
+#define MT_TXD1_BIP BIT(24)
+#define MT_TXD1_ETH_802_3 BIT(20)
+#define MT_TXD1_HDR_INFO GENMASK(20, 16)
+#define MT_TXD1_HDR_FORMAT GENMASK(15, 14)
+#define MT_TXD1_TGID GENMASK(13, 12)
+#define MT_TXD1_WLAN_IDX GENMASK(11, 0)
+
+#define MT_TXD2_POWER_OFFSET GENMASK(31, 26)
+#define MT_TXD2_MAX_TX_TIME GENMASK(25, 16)
+#define MT_TXD2_FRAG GENMASK(15, 14)
+#define MT_TXD2_HTC_VLD BIT(13)
+#define MT_TXD2_DURATION BIT(12)
+#define MT_TXD2_HDR_PAD GENMASK(11, 10)
+#define MT_TXD2_RTS BIT(9)
+#define MT_TXD2_OWN_MAC_MAP BIT(8)
+#define MT_TXD2_BF_TYPE GENMASK(6, 7)
+#define MT_TXD2_FRAME_TYPE GENMASK(5, 4)
+#define MT_TXD2_SUB_TYPE GENMASK(3, 0)
+
+#define MT_TXD3_SN_VALID BIT(31)
+#define MT_TXD3_PN_VALID BIT(30)
+#define MT_TXD3_SW_POWER_MGMT BIT(29)
+#define MT_TXD3_BA_DISABLE BIT(28)
+#define MT_TXD3_SEQ GENMASK(27, 16)
+#define MT_TXD3_REM_TX_COUNT GENMASK(15, 11)
+#define MT_TXD3_TX_COUNT GENMASK(10, 6)
+#define MT_TXD3_HW_AMSDU BIT(5)
+#define MT_TXD3_BCM BIT(4)
+#define MT_TXD3_EEOSP BIT(3)
+#define MT_TXD3_EMRD BIT(2)
+#define MT_TXD3_PROTECT_FRAME BIT(1)
+#define MT_TXD3_NO_ACK BIT(0)
+
+#define MT_TXD4_PN_LOW GENMASK(31, 0)
+
+#define MT_TXD5_PN_HIGH GENMASK(31, 16)
+#define MT_TXD5_FL BIT(15)
+#define MT_TXD5_BYPASS_TBB BIT(14)
+#define MT_TXD5_BYPASS_RBB BIT(13)
+#define MT_TXD5_BSS_COLOR_ZERO BIT(12)
+#define MT_TXD5_TX_STATUS_HOST BIT(10)
+#define MT_TXD5_TX_STATUS_MCU BIT(9)
+#define MT_TXD5_TX_STATUS_FMT BIT(8)
+#define MT_TXD5_PID GENMASK(7, 0)
+
+#define MT_TXD6_TX_SRC GENMASK(31, 30)
+#define MT_TXD6_VTA BIT(28)
+#define MT_TXD6_FIXED_BW BIT(25)
+#define MT_TXD6_BW GENMASK(24, 22)
+#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_MSDU_CNT GENMASK(9, 4)
+#define MT_TXD6_SPE_ID_IDX BIT(10)
+#define MT_TXD6_ANT_ID GENMASK(7, 4)
+#define MT_TXD6_DIS_MAT BIT(3)
+#define MT_TXD6_DAS BIT(2)
+#define MT_TXD6_AMSDU_CAP BIT(1)
+
+#define MT_TXD7_TXD_LEN GENMASK(31, 30)
+#define MT_TXD7_IP_SUM BIT(29)
+#define MT_TXD7_DROP_BY_SDO BIT(28)
+#define MT_TXD7_MAC_TXD BIT(27)
+#define MT_TXD7_CTXD BIT(26)
+#define MT_TXD7_CTXD_CNT GENMASK(25, 22)
+#define MT_TXD7_UDP_TCP_SUM BIT(15)
+#define MT_TXD7_TX_TIME GENMASK(9, 0)
+
+#define MT_TX_RATE_STBC BIT(13)
+#define MT_TX_RATE_NSS GENMASK(13, 10)
+#define MT_TX_RATE_MODE GENMASK(9, 6)
+#define MT_TX_RATE_SU_EXT_TONE BIT(5)
+#define MT_TX_RATE_DCM BIT(4)
+/* VHT/HE only use bits 0-3 */
+#define MT_TX_RATE_IDX GENMASK(5, 0)
+
+struct mt7996_txp {
+ __le16 flags;
+ __le16 token;
+ u8 bss_idx;
+ __le16 rept_wds_wcid;
+ u8 nbuf;
+#define MT_TXP_MAX_BUF_NUM 6
+ __le32 buf[MT_TXP_MAX_BUF_NUM];
+ __le16 len[MT_TXP_MAX_BUF_NUM];
+} __packed __aligned(4);
+
+#define MT_TXFREE0_PKT_TYPE GENMASK(31, 27)
+#define MT_TXFREE0_MSDU_CNT GENMASK(25, 16)
+#define MT_TXFREE0_RX_BYTE GENMASK(15, 0)
+
+#define MT_TXFREE1_VER GENMASK(18, 16)
+
+#define MT_TXFREE_INFO_PAIR BIT(31)
+#define MT_TXFREE_INFO_HEADER BIT(30)
+#define MT_TXFREE_INFO_WLAN_ID GENMASK(23, 12)
+#define MT_TXFREE_INFO_MSDU_ID GENMASK(14, 0)
+
+#define MT_TXS0_BW GENMASK(31, 29)
+#define MT_TXS0_TID GENMASK(28, 26)
+#define MT_TXS0_AMPDU BIT(25)
+#define MT_TXS0_TXS_FORMAT GENMASK(24, 23)
+#define MT_TXS0_BA_ERROR BIT(22)
+#define MT_TXS0_PS_FLAG BIT(21)
+#define MT_TXS0_TXOP_TIMEOUT BIT(20)
+#define MT_TXS0_BIP_ERROR BIT(19)
+
+#define MT_TXS0_QUEUE_TIMEOUT BIT(18)
+#define MT_TXS0_RTS_TIMEOUT BIT(17)
+#define MT_TXS0_ACK_TIMEOUT BIT(16)
+#define MT_TXS0_ACK_ERROR_MASK GENMASK(18, 16)
+
+#define MT_TXS0_TX_STATUS_HOST BIT(15)
+#define MT_TXS0_TX_STATUS_MCU BIT(14)
+#define MT_TXS0_TX_RATE GENMASK(13, 0)
+
+#define MT_TXS1_SEQNO GENMASK(31, 20)
+#define MT_TXS1_RESP_RATE GENMASK(19, 16)
+#define MT_TXS1_RXV_SEQNO GENMASK(15, 8)
+#define MT_TXS1_TX_POWER_DBM GENMASK(7, 0)
+
+#define MT_TXS2_BF_STATUS GENMASK(31, 30)
+#define MT_TXS2_BAND GENMASK(29, 28)
+#define MT_TXS2_WCID GENMASK(27, 16)
+#define MT_TXS2_TX_DELAY GENMASK(15, 0)
+
+#define MT_TXS3_PID GENMASK(31, 24)
+#define MT_TXS3_RATE_STBC BIT(7)
+#define MT_TXS3_FIXED_RATE BIT(6)
+#define MT_TXS3_SRC GENMASK(5, 4)
+#define MT_TXS3_SHARED_ANTENNA BIT(3)
+#define MT_TXS3_LAST_TX_RATE GENMASK(2, 0)
+
+#define MT_TXS4_TIMESTAMP GENMASK(31, 0)
+
+#define MT_TXS5_F0_FINAL_MPDU BIT(31)
+#define MT_TXS5_F0_QOS BIT(30)
+#define MT_TXS5_F0_TX_COUNT GENMASK(29, 25)
+#define MT_TXS5_F0_FRONT_TIME GENMASK(24, 0)
+#define MT_TXS5_F1_MPDU_TX_COUNT GENMASK(31, 24)
+#define MT_TXS5_F1_MPDU_TX_BYTES GENMASK(23, 0)
+
+#define MT_TXS6_F0_NOISE_3 GENMASK(31, 24)
+#define MT_TXS6_F0_NOISE_2 GENMASK(23, 16)
+#define MT_TXS6_F0_NOISE_1 GENMASK(15, 8)
+#define MT_TXS6_F0_NOISE_0 GENMASK(7, 0)
+#define MT_TXS6_F1_MPDU_FAIL_COUNT GENMASK(31, 24)
+#define MT_TXS6_F1_MPDU_FAIL_BYTES GENMASK(23, 0)
+
+#define MT_TXS7_F0_RCPI_3 GENMASK(31, 24)
+#define MT_TXS7_F0_RCPI_2 GENMASK(23, 16)
+#define MT_TXS7_F0_RCPI_1 GENMASK(15, 8)
+#define MT_TXS7_F0_RCPI_0 GENMASK(7, 0)
+#define MT_TXS7_F1_MPDU_RETRY_COUNT GENMASK(31, 24)
+#define MT_TXS7_F1_MPDU_RETRY_BYTES GENMASK(23, 0)
+
+struct mt7996_dfs_pulse {
+ u32 max_width; /* us */
+ int max_pwr; /* dbm */
+ int min_pwr; /* dbm */
+ u32 min_stgr_pri; /* us */
+ u32 max_stgr_pri; /* us */
+ u32 min_cr_pri; /* us */
+ u32 max_cr_pri; /* us */
+};
+
+struct mt7996_dfs_pattern {
+ u8 enb;
+ u8 stgr;
+ u8 min_crpn;
+ u8 max_crpn;
+ u8 min_crpr;
+ u8 min_pw;
+ u32 min_pri;
+ u32 max_pri;
+ u8 max_pw;
+ u8 min_crbn;
+ u8 max_crbn;
+ u8 min_stgpn;
+ u8 max_stgpn;
+ u8 min_stgpr;
+ u8 rsv[2];
+ u32 min_stgpr_diff;
+} __packed;
+
+struct mt7996_dfs_radar_spec {
+ struct mt7996_dfs_pulse pulse_th;
+ struct mt7996_dfs_pattern radar_pattern[16];
+};
+
+static inline struct mt7996_txp *
+mt7996_txwi_to_txp(struct mt76_dev *dev, struct mt76_txwi_cache *t)
+{
+ u8 *txwi;
+
+ if (!t)
+ return NULL;
+
+ txwi = mt76_get_txwi_ptr(dev, t);
+
+ return (struct mt7996_txp *)(txwi + MT_TXD_SIZE);
+}
+
+#endif
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c
new file mode 100644
index 000000000000..4421cd54311b
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c
@@ -0,0 +1,1334 @@
+// SPDX-License-Identifier: ISC
+/*
+ * Copyright (C) 2022 MediaTek Inc.
+ */
+
+#include "mt7996.h"
+#include "mcu.h"
+
+static bool mt7996_dev_running(struct mt7996_dev *dev)
+{
+ struct mt7996_phy *phy;
+
+ if (test_bit(MT76_STATE_RUNNING, &dev->mphy.state))
+ return true;
+
+ phy = mt7996_phy2(dev);
+ if (phy && test_bit(MT76_STATE_RUNNING, &phy->mt76->state))
+ return true;
+
+ phy = mt7996_phy3(dev);
+
+ return phy && test_bit(MT76_STATE_RUNNING, &phy->mt76->state);
+}
+
+static int mt7996_start(struct ieee80211_hw *hw)
+{
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ bool running;
+ int ret;
+
+ flush_work(&dev->init_work);
+
+ mutex_lock(&dev->mt76.mutex);
+
+ running = mt7996_dev_running(dev);
+ if (!running) {
+ ret = mt7996_mcu_set_hdr_trans(dev, true);
+ if (ret)
+ goto out;
+ }
+
+ mt7996_mac_enable_nf(dev, phy->mt76->band_idx);
+
+ ret = mt7996_mcu_set_rts_thresh(phy, 0x92b);
+ if (ret)
+ goto out;
+
+ ret = mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_RX_PATH);
+ if (ret)
+ goto out;
+
+ set_bit(MT76_STATE_RUNNING, &phy->mt76->state);
+
+ ieee80211_iterate_interfaces(dev->mt76.hw,
+ IEEE80211_IFACE_ITER_RESUME_ALL,
+ mt7996_mcu_set_pm, dev->mt76.hw);
+
+ ieee80211_queue_delayed_work(hw, &phy->mt76->mac_work,
+ MT7996_WATCHDOG_TIME);
+
+ if (!running)
+ mt7996_mac_reset_counters(phy);
+
+out:
+ mutex_unlock(&dev->mt76.mutex);
+
+ return ret;
+}
+
+static void mt7996_stop(struct ieee80211_hw *hw)
+{
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+
+ cancel_delayed_work_sync(&phy->mt76->mac_work);
+
+ mutex_lock(&dev->mt76.mutex);
+
+ clear_bit(MT76_STATE_RUNNING, &phy->mt76->state);
+
+ ieee80211_iterate_interfaces(dev->mt76.hw,
+ IEEE80211_IFACE_ITER_RESUME_ALL,
+ mt7996_mcu_set_pm, dev->mt76.hw);
+
+ mutex_unlock(&dev->mt76.mutex);
+}
+
+static inline int get_free_idx(u32 mask, u8 start, u8 end)
+{
+ return ffs(~mask & GENMASK(end, start));
+}
+
+static int get_omac_idx(enum nl80211_iftype type, u64 mask)
+{
+ int i;
+
+ switch (type) {
+ case NL80211_IFTYPE_MESH_POINT:
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_STATION:
+ /* prefer hw bssid slot 1-3 */
+ i = get_free_idx(mask, HW_BSSID_1, HW_BSSID_3);
+ if (i)
+ return i - 1;
+
+ if (type != NL80211_IFTYPE_STATION)
+ break;
+
+ i = get_free_idx(mask, EXT_BSSID_1, EXT_BSSID_MAX);
+ if (i)
+ return i - 1;
+
+ if (~mask & BIT(HW_BSSID_0))
+ return HW_BSSID_0;
+
+ break;
+ case NL80211_IFTYPE_MONITOR:
+ case NL80211_IFTYPE_AP:
+ /* ap uses hw bssid 0 and ext bssid */
+ if (~mask & BIT(HW_BSSID_0))
+ return HW_BSSID_0;
+
+ i = get_free_idx(mask, EXT_BSSID_1, EXT_BSSID_MAX);
+ if (i)
+ return i - 1;
+
+ break;
+ default:
+ WARN_ON(1);
+ break;
+ }
+
+ return -1;
+}
+
+static void mt7996_init_bitrate_mask(struct ieee80211_vif *vif)
+{
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mvif->bitrate_mask.control); i++) {
+ mvif->bitrate_mask.control[i].gi = NL80211_TXRATE_DEFAULT_GI;
+ mvif->bitrate_mask.control[i].he_gi = 0xff;
+ mvif->bitrate_mask.control[i].he_ltf = 0xff;
+ mvif->bitrate_mask.control[i].legacy = GENMASK(31, 0);
+ memset(mvif->bitrate_mask.control[i].ht_mcs, 0xff,
+ sizeof(mvif->bitrate_mask.control[i].ht_mcs));
+ memset(mvif->bitrate_mask.control[i].vht_mcs, 0xff,
+ sizeof(mvif->bitrate_mask.control[i].vht_mcs));
+ memset(mvif->bitrate_mask.control[i].he_mcs, 0xff,
+ sizeof(mvif->bitrate_mask.control[i].he_mcs));
+ }
+}
+
+static int mt7996_add_interface(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_hw_phy(hw);
+ struct mt76_txq *mtxq;
+ u8 band_idx = phy->mt76->band_idx;
+ int idx, ret = 0;
+
+ mutex_lock(&dev->mt76.mutex);
+
+ if (vif->type == NL80211_IFTYPE_MONITOR &&
+ is_zero_ether_addr(vif->addr))
+ phy->monitor_vif = vif;
+
+ mvif->mt76.idx = __ffs64(~dev->mt76.vif_mask);
+ if (mvif->mt76.idx >= (MT7996_MAX_INTERFACES << dev->dbdc_support)) {
+ ret = -ENOSPC;
+ goto out;
+ }
+
+ idx = get_omac_idx(vif->type, phy->omac_mask);
+ if (idx < 0) {
+ ret = -ENOSPC;
+ goto out;
+ }
+ mvif->mt76.omac_idx = idx;
+ mvif->phy = phy;
+ mvif->mt76.band_idx = band_idx;
+ mvif->mt76.wmm_idx = band_idx;
+
+ ret = mt7996_mcu_add_dev_info(phy, vif, true);
+ if (ret)
+ goto out;
+
+ ret = mt7996_mcu_set_radio_en(phy, true);
+ if (ret)
+ goto out;
+
+ dev->mt76.vif_mask |= BIT_ULL(mvif->mt76.idx);
+ phy->omac_mask |= BIT_ULL(mvif->mt76.omac_idx);
+
+ idx = MT7996_WTBL_RESERVED - mvif->mt76.idx;
+
+ INIT_LIST_HEAD(&mvif->sta.rc_list);
+ INIT_LIST_HEAD(&mvif->sta.poll_list);
+ mvif->sta.wcid.idx = idx;
+ mvif->sta.wcid.phy_idx = band_idx;
+ mvif->sta.wcid.hw_key_idx = -1;
+ mvif->sta.wcid.tx_info |= MT_WCID_TX_INFO_SET;
+ mt76_packet_id_init(&mvif->sta.wcid);
+
+ mt7996_mac_wtbl_update(dev, idx,
+ MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
+
+ if (vif->txq) {
+ mtxq = (struct mt76_txq *)vif->txq->drv_priv;
+ mtxq->wcid = idx;
+ }
+
+ if (vif->type != NL80211_IFTYPE_AP &&
+ (!mvif->mt76.omac_idx || mvif->mt76.omac_idx > 3))
+ vif->offload_flags = 0;
+ vif->offload_flags |= IEEE80211_OFFLOAD_ENCAP_4ADDR;
+
+ mt7996_init_bitrate_mask(vif);
+ memset(&mvif->cap, -1, sizeof(mvif->cap));
+
+ mt7996_mcu_add_bss_info(phy, vif, true);
+ mt7996_mcu_add_sta(dev, vif, NULL, true);
+ rcu_assign_pointer(dev->mt76.wcid[idx], &mvif->sta.wcid);
+
+out:
+ mutex_unlock(&dev->mt76.mutex);
+
+ return ret;
+}
+
+static void mt7996_remove_interface(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_sta *msta = &mvif->sta;
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ int idx = msta->wcid.idx;
+
+ mt7996_mcu_add_bss_info(phy, vif, false);
+ mt7996_mcu_add_sta(dev, vif, NULL, false);
+
+ if (vif == phy->monitor_vif)
+ phy->monitor_vif = NULL;
+
+ mt7996_mcu_add_dev_info(phy, vif, false);
+ mt7996_mcu_set_radio_en(phy, false);
+
+ rcu_assign_pointer(dev->mt76.wcid[idx], NULL);
+
+ mutex_lock(&dev->mt76.mutex);
+ dev->mt76.vif_mask &= ~BIT_ULL(mvif->mt76.idx);
+ phy->omac_mask &= ~BIT_ULL(mvif->mt76.omac_idx);
+ mutex_unlock(&dev->mt76.mutex);
+
+ spin_lock_bh(&dev->sta_poll_lock);
+ if (!list_empty(&msta->poll_list))
+ list_del_init(&msta->poll_list);
+ spin_unlock_bh(&dev->sta_poll_lock);
+
+ mt76_packet_id_flush(&dev->mt76, &msta->wcid);
+}
+
+int mt7996_set_channel(struct mt7996_phy *phy)
+{
+ struct mt7996_dev *dev = phy->dev;
+ int ret;
+
+ cancel_delayed_work_sync(&phy->mt76->mac_work);
+
+ mutex_lock(&dev->mt76.mutex);
+ set_bit(MT76_RESET, &phy->mt76->state);
+
+ mt76_set_channel(phy->mt76);
+
+ ret = mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_SWITCH);
+ if (ret)
+ goto out;
+
+ mt7996_mac_set_timing(phy);
+ ret = mt7996_dfs_init_radar_detector(phy);
+ mt7996_mac_cca_stats_reset(phy);
+
+ mt7996_mac_reset_counters(phy);
+ phy->noise = 0;
+
+out:
+ clear_bit(MT76_RESET, &phy->mt76->state);
+ mutex_unlock(&dev->mt76.mutex);
+
+ mt76_txq_schedule_all(phy->mt76);
+
+ ieee80211_queue_delayed_work(phy->mt76->hw,
+ &phy->mt76->mac_work,
+ MT7996_WATCHDOG_TIME);
+
+ return ret;
+}
+
+static int mt7996_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 mt7996_dev *dev = mt7996_hw_dev(hw);
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_sta *msta = sta ? (struct mt7996_sta *)sta->drv_priv :
+ &mvif->sta;
+ struct mt76_wcid *wcid = &msta->wcid;
+ u8 *wcid_keyidx = &wcid->hw_key_idx;
+ int idx = key->keyidx;
+ int err = 0;
+
+ /* The hardware does not support per-STA RX GTK, fallback
+ * to software mode for these.
+ */
+ if ((vif->type == NL80211_IFTYPE_ADHOC ||
+ vif->type == NL80211_IFTYPE_MESH_POINT) &&
+ (key->cipher == WLAN_CIPHER_SUITE_TKIP ||
+ key->cipher == WLAN_CIPHER_SUITE_CCMP) &&
+ !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE))
+ return -EOPNOTSUPP;
+
+ /* fall back to sw encryption for unsupported ciphers */
+ switch (key->cipher) {
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ wcid_keyidx = &wcid->hw_key_idx2;
+ key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIE;
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ case WLAN_CIPHER_SUITE_CCMP:
+ case WLAN_CIPHER_SUITE_CCMP_256:
+ case WLAN_CIPHER_SUITE_GCMP:
+ case WLAN_CIPHER_SUITE_GCMP_256:
+ case WLAN_CIPHER_SUITE_SMS4:
+ break;
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ mutex_lock(&dev->mt76.mutex);
+
+ if (cmd == SET_KEY && !sta && !mvif->mt76.cipher) {
+ mvif->mt76.cipher = mt76_connac_mcu_get_cipher(key->cipher);
+ mt7996_mcu_add_bss_info(phy, vif, true);
+ }
+
+ if (cmd == SET_KEY)
+ *wcid_keyidx = idx;
+ else if (idx == *wcid_keyidx)
+ *wcid_keyidx = -1;
+ else
+ goto out;
+
+ mt76_wcid_key_setup(&dev->mt76, wcid,
+ cmd == SET_KEY ? key : NULL);
+
+ err = mt7996_mcu_add_key(&dev->mt76, vif, &msta->bip,
+ key, MCU_WMWA_UNI_CMD(STA_REC_UPDATE),
+ &msta->wcid, cmd);
+out:
+ mutex_unlock(&dev->mt76.mutex);
+
+ return err;
+}
+
+static int mt7996_config(struct ieee80211_hw *hw, u32 changed)
+{
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ int ret;
+
+ if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
+ ieee80211_stop_queues(hw);
+ ret = mt7996_set_channel(phy);
+ if (ret)
+ return ret;
+ ieee80211_wake_queues(hw);
+ }
+
+ mutex_lock(&dev->mt76.mutex);
+
+ if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
+ bool enabled = !!(hw->conf.flags & IEEE80211_CONF_MONITOR);
+
+ if (!enabled)
+ phy->rxfilter |= MT_WF_RFCR_DROP_OTHER_UC;
+ else
+ phy->rxfilter &= ~MT_WF_RFCR_DROP_OTHER_UC;
+
+ mt76_rmw_field(dev, MT_DMA_DCR0(phy->mt76->band_idx),
+ MT_DMA_DCR0_RXD_G5_EN, enabled);
+ mt76_wr(dev, MT_WF_RFCR(phy->mt76->band_idx), phy->rxfilter);
+ }
+
+ mutex_unlock(&dev->mt76.mutex);
+
+ return 0;
+}
+
+static int
+mt7996_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ unsigned int link_id, u16 queue,
+ const struct ieee80211_tx_queue_params *params)
+{
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+
+ /* no need to update right away, we'll get BSS_CHANGED_QOS */
+ queue = mt76_connac_lmac_mapping(queue);
+ mvif->queue_params[queue] = *params;
+
+ return 0;
+}
+
+static void mt7996_configure_filter(struct ieee80211_hw *hw,
+ unsigned int changed_flags,
+ unsigned int *total_flags,
+ u64 multicast)
+{
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ u32 ctl_flags = MT_WF_RFCR1_DROP_ACK |
+ MT_WF_RFCR1_DROP_BF_POLL |
+ MT_WF_RFCR1_DROP_BA |
+ MT_WF_RFCR1_DROP_CFEND |
+ MT_WF_RFCR1_DROP_CFACK;
+ u32 flags = 0;
+
+#define MT76_FILTER(_flag, _hw) do { \
+ flags |= *total_flags & FIF_##_flag; \
+ phy->rxfilter &= ~(_hw); \
+ phy->rxfilter |= !(flags & FIF_##_flag) * (_hw); \
+ } while (0)
+
+ mutex_lock(&dev->mt76.mutex);
+
+ phy->rxfilter &= ~(MT_WF_RFCR_DROP_OTHER_BSS |
+ MT_WF_RFCR_DROP_OTHER_BEACON |
+ MT_WF_RFCR_DROP_FRAME_REPORT |
+ MT_WF_RFCR_DROP_PROBEREQ |
+ MT_WF_RFCR_DROP_MCAST_FILTERED |
+ MT_WF_RFCR_DROP_MCAST |
+ MT_WF_RFCR_DROP_BCAST |
+ MT_WF_RFCR_DROP_DUPLICATE |
+ MT_WF_RFCR_DROP_A2_BSSID |
+ MT_WF_RFCR_DROP_UNWANTED_CTL |
+ MT_WF_RFCR_DROP_STBC_MULTI);
+
+ MT76_FILTER(OTHER_BSS, MT_WF_RFCR_DROP_OTHER_TIM |
+ MT_WF_RFCR_DROP_A3_MAC |
+ MT_WF_RFCR_DROP_A3_BSSID);
+
+ MT76_FILTER(FCSFAIL, MT_WF_RFCR_DROP_FCSFAIL);
+
+ MT76_FILTER(CONTROL, MT_WF_RFCR_DROP_CTS |
+ MT_WF_RFCR_DROP_RTS |
+ MT_WF_RFCR_DROP_CTL_RSV |
+ MT_WF_RFCR_DROP_NDPA);
+
+ *total_flags = flags;
+ mt76_wr(dev, MT_WF_RFCR(phy->mt76->band_idx), phy->rxfilter);
+
+ if (*total_flags & FIF_CONTROL)
+ mt76_clear(dev, MT_WF_RFCR1(phy->mt76->band_idx), ctl_flags);
+ else
+ mt76_set(dev, MT_WF_RFCR1(phy->mt76->band_idx), ctl_flags);
+
+ mutex_unlock(&dev->mt76.mutex);
+}
+
+static void
+mt7996_update_bss_color(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct cfg80211_he_bss_color *bss_color)
+{
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+
+ switch (vif->type) {
+ case NL80211_IFTYPE_AP: {
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+
+ if (mvif->mt76.omac_idx > HW_BSSID_MAX)
+ return;
+ fallthrough;
+ }
+ case NL80211_IFTYPE_STATION:
+ mt7996_mcu_update_bss_color(dev, vif, bss_color);
+ break;
+ default:
+ break;
+ }
+}
+
+static void mt7996_bss_info_changed(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *info,
+ u64 changed)
+{
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+
+ mutex_lock(&dev->mt76.mutex);
+
+ /* station mode uses BSSID to map the wlan entry to a peer,
+ * and then peer references bss_info_rfch to set bandwidth cap.
+ */
+ if (changed & BSS_CHANGED_BSSID &&
+ vif->type == NL80211_IFTYPE_STATION) {
+ bool join = !is_zero_ether_addr(info->bssid);
+
+ mt7996_mcu_add_bss_info(phy, vif, join);
+ mt7996_mcu_add_sta(dev, vif, NULL, join);
+ }
+
+ if (changed & BSS_CHANGED_ASSOC)
+ mt7996_mcu_add_bss_info(phy, vif, vif->cfg.assoc);
+
+ 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;
+
+ if (slottime != phy->slottime) {
+ phy->slottime = slottime;
+ mt7996_mac_set_timing(phy);
+ }
+ }
+
+ if (changed & BSS_CHANGED_BEACON_ENABLED && info->enable_beacon) {
+ mt7996_mcu_add_bss_info(phy, vif, true);
+ mt7996_mcu_add_sta(dev, vif, NULL, true);
+ }
+
+ /* ensure that enable txcmd_mode after bss_info */
+ if (changed & (BSS_CHANGED_QOS | BSS_CHANGED_BEACON_ENABLED))
+ mt7996_mcu_set_tx(dev, vif);
+
+ if (changed & BSS_CHANGED_HE_OBSS_PD)
+ mt7996_mcu_add_obss_spr(phy, vif, &info->he_obss_pd);
+
+ if (changed & BSS_CHANGED_HE_BSS_COLOR)
+ mt7996_update_bss_color(hw, vif, &info->he_bss_color);
+
+ if (changed & (BSS_CHANGED_BEACON |
+ BSS_CHANGED_BEACON_ENABLED))
+ mt7996_mcu_add_beacon(hw, vif, info->enable_beacon);
+
+ if (changed & BSS_CHANGED_UNSOL_BCAST_PROBE_RESP ||
+ changed & BSS_CHANGED_FILS_DISCOVERY)
+ mt7996_mcu_beacon_inband_discov(dev, vif, changed);
+
+ mutex_unlock(&dev->mt76.mutex);
+}
+
+static void
+mt7996_channel_switch_beacon(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct cfg80211_chan_def *chandef)
+{
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+
+ mutex_lock(&dev->mt76.mutex);
+ mt7996_mcu_add_beacon(hw, vif, true);
+ mutex_unlock(&dev->mt76.mutex);
+}
+
+int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ 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;
+ u8 band_idx = mvif->phy->mt76->band_idx;
+ int ret, 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->poll_list);
+ msta->vif = mvif;
+ msta->wcid.sta = 1;
+ msta->wcid.idx = idx;
+ msta->wcid.phy_idx = band_idx;
+ msta->wcid.tx_info |= MT_WCID_TX_INFO_SET;
+ msta->jiffies = jiffies;
+
+ ewma_avg_signal_init(&msta->avg_ack_signal);
+
+ mt7996_mac_wtbl_update(dev, idx,
+ MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
+
+ ret = mt7996_mcu_add_sta(dev, vif, sta, true);
+ if (ret)
+ return ret;
+
+ return mt7996_mcu_add_rate_ctrl(dev, vif, sta, false);
+}
+
+void mt7996_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ int i;
+
+ mt7996_mcu_add_sta(dev, vif, sta, false);
+
+ mt7996_mac_wtbl_update(dev, msta->wcid.idx,
+ MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
+
+ for (i = 0; i < ARRAY_SIZE(msta->twt.flow); i++)
+ mt7996_mac_twt_teardown_flow(dev, msta, i);
+
+ spin_lock_bh(&dev->sta_poll_lock);
+ if (!list_empty(&msta->poll_list))
+ list_del_init(&msta->poll_list);
+ if (!list_empty(&msta->rc_list))
+ list_del_init(&msta->rc_list);
+ spin_unlock_bh(&dev->sta_poll_lock);
+}
+
+static void mt7996_tx(struct ieee80211_hw *hw,
+ struct ieee80211_tx_control *control,
+ struct sk_buff *skb)
+{
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ struct mt76_phy *mphy = hw->priv;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_vif *vif = info->control.vif;
+ struct mt76_wcid *wcid = &dev->mt76.global_wcid;
+
+ if (control->sta) {
+ struct mt7996_sta *sta;
+
+ sta = (struct mt7996_sta *)control->sta->drv_priv;
+ wcid = &sta->wcid;
+ }
+
+ if (vif && !control->sta) {
+ struct mt7996_vif *mvif;
+
+ mvif = (struct mt7996_vif *)vif->drv_priv;
+ wcid = &mvif->sta.wcid;
+ }
+
+ mt76_tx(mphy, control->sta, wcid, skb);
+}
+
+static int mt7996_set_rts_threshold(struct ieee80211_hw *hw, u32 val)
+{
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ int ret;
+
+ mutex_lock(&phy->dev->mt76.mutex);
+ ret = mt7996_mcu_set_rts_thresh(phy, val);
+ mutex_unlock(&phy->dev->mt76.mutex);
+
+ return ret;
+}
+
+static int
+mt7996_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_ampdu_params *params)
+{
+ 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;
+ u16 tid = params->tid;
+ u16 ssn = params->ssn;
+ struct mt76_txq *mtxq;
+ int ret = 0;
+
+ if (!txq)
+ return -EINVAL;
+
+ 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->ampdu_state);
+ ret = mt7996_mcu_add_tx_ba(dev, params, false);
+ break;
+ case IEEE80211_AMPDU_TX_START:
+ set_bit(tid, &msta->ampdu_state);
+ ret = IEEE80211_AMPDU_TX_START_IMMEDIATE;
+ break;
+ case IEEE80211_AMPDU_TX_STOP_CONT:
+ mtxq->aggr = false;
+ clear_bit(tid, &msta->ampdu_state);
+ ret = mt7996_mcu_add_tx_ba(dev, params, false);
+ ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+ break;
+ }
+ mutex_unlock(&dev->mt76.mutex);
+
+ return ret;
+}
+
+static int
+mt7996_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ return mt76_sta_state(hw, vif, sta, IEEE80211_STA_NOTEXIST,
+ IEEE80211_STA_NONE);
+}
+
+static int
+mt7996_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ return mt76_sta_state(hw, vif, sta, IEEE80211_STA_NONE,
+ IEEE80211_STA_NOTEXIST);
+}
+
+static int
+mt7996_get_stats(struct ieee80211_hw *hw,
+ struct ieee80211_low_level_stats *stats)
+{
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ struct mib_stats *mib = &phy->mib;
+
+ mutex_lock(&dev->mt76.mutex);
+
+ stats->dot11RTSSuccessCount = mib->rts_cnt;
+ stats->dot11RTSFailureCount = mib->rts_retries_cnt;
+ stats->dot11FCSErrorCount = mib->fcs_err_cnt;
+ stats->dot11ACKFailureCount = mib->ack_fail_cnt;
+
+ mutex_unlock(&dev->mt76.mutex);
+
+ return 0;
+}
+
+u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_vif *mvif)
+{
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ union {
+ u64 t64;
+ u32 t32[2];
+ } tsf;
+ u16 n;
+
+ lockdep_assert_held(&dev->mt76.mutex);
+
+ n = mvif->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0
+ : mvif->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);
+ tsf.t32[0] = mt76_rr(dev, MT_LPON_UTTR0(phy->mt76->band_idx));
+ tsf.t32[1] = mt76_rr(dev, MT_LPON_UTTR1(phy->mt76->band_idx));
+
+ return tsf.t64;
+}
+
+static u64
+mt7996_get_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);
+ u64 ret;
+
+ mutex_lock(&dev->mt76.mutex);
+ ret = __mt7996_get_tsf(hw, mvif);
+ mutex_unlock(&dev->mt76.mutex);
+
+ return ret;
+}
+
+static void
+mt7996_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ u64 timestamp)
+{
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ union {
+ u64 t64;
+ u32 t32[2];
+ } tsf = { .t64 = timestamp, };
+ u16 n;
+
+ mutex_lock(&dev->mt76.mutex);
+
+ n = mvif->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0
+ : mvif->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 overwrite */
+ mt76_rmw(dev, MT_LPON_TCR(phy->mt76->band_idx, n), MT_LPON_TCR_SW_MODE,
+ MT_LPON_TCR_SW_WRITE);
+
+ mutex_unlock(&dev->mt76.mutex);
+}
+
+static void
+mt7996_offset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ s64 timestamp)
+{
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ union {
+ u64 t64;
+ u32 t32[2];
+ } tsf = { .t64 = timestamp, };
+ u16 n;
+
+ mutex_lock(&dev->mt76.mutex);
+
+ n = mvif->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0
+ : mvif->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);
+
+ mutex_unlock(&dev->mt76.mutex);
+}
+
+static void
+mt7996_set_coverage_class(struct ieee80211_hw *hw, s16 coverage_class)
+{
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ struct mt7996_dev *dev = phy->dev;
+
+ mutex_lock(&dev->mt76.mutex);
+ phy->coverage_class = max_t(s16, coverage_class, 0);
+ mt7996_mac_set_timing(phy);
+ mutex_unlock(&dev->mt76.mutex);
+}
+
+static int
+mt7996_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant)
+{
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ int max_nss = hweight8(hw->wiphy->available_antennas_tx);
+ u8 band_idx = phy->mt76->band_idx, shift = dev->chainshift[band_idx];
+
+ if (!tx_ant || tx_ant != rx_ant || ffs(tx_ant) > max_nss)
+ return -EINVAL;
+
+ if ((BIT(hweight8(tx_ant)) - 1) != tx_ant)
+ tx_ant = BIT(ffs(tx_ant) - 1) - 1;
+
+ mutex_lock(&dev->mt76.mutex);
+
+ phy->mt76->antenna_mask = tx_ant;
+
+ /* restore to the origin chainmask which might have auxiliary path */
+ if (hweight8(tx_ant) == max_nss)
+ phy->mt76->chainmask = (dev->chainmask >> shift) << shift;
+ else
+ phy->mt76->chainmask = tx_ant << shift;
+
+ mt76_set_stream_caps(phy->mt76, true);
+ mt7996_set_stream_vht_txbf_caps(phy);
+ mt7996_set_stream_he_caps(phy);
+
+ mutex_unlock(&dev->mt76.mutex);
+
+ return 0;
+}
+
+static void mt7996_sta_statistics(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct station_info *sinfo)
+{
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ struct rate_info *txrate = &msta->wcid.rate;
+
+ if (!txrate->legacy && !txrate->flags)
+ return;
+
+ if (txrate->legacy) {
+ sinfo->txrate.legacy = txrate->legacy;
+ } else {
+ sinfo->txrate.mcs = txrate->mcs;
+ sinfo->txrate.nss = txrate->nss;
+ sinfo->txrate.bw = txrate->bw;
+ sinfo->txrate.he_gi = txrate->he_gi;
+ sinfo->txrate.he_dcm = txrate->he_dcm;
+ sinfo->txrate.he_ru_alloc = txrate->he_ru_alloc;
+ }
+ sinfo->txrate.flags = txrate->flags;
+ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE);
+
+ sinfo->ack_signal = (s8)msta->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->filled |= BIT_ULL(NL80211_STA_INFO_ACK_SIGNAL_AVG);
+}
+
+static void mt7996_sta_rc_work(void *data, struct ieee80211_sta *sta)
+{
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ struct mt7996_dev *dev = msta->vif->phy->dev;
+ u32 *changed = data;
+
+ spin_lock_bh(&dev->sta_poll_lock);
+ msta->changed |= *changed;
+ if (list_empty(&msta->rc_list))
+ list_add_tail(&msta->rc_list, &dev->sta_rc_list);
+ spin_unlock_bh(&dev->sta_poll_lock);
+}
+
+static void mt7996_sta_rc_update(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ u32 changed)
+{
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ struct mt7996_dev *dev = phy->dev;
+
+ mt7996_sta_rc_work(&changed, sta);
+ ieee80211_queue_work(hw, &dev->rc_work);
+}
+
+static int
+mt7996_set_bitrate_mask(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ const struct cfg80211_bitrate_mask *mask)
+{
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ struct mt7996_dev *dev = phy->dev;
+ u32 changed = IEEE80211_RC_SUPP_RATES_CHANGED;
+
+ mvif->bitrate_mask = *mask;
+
+ /* if multiple rates across different preambles are given we can
+ * reconfigure this info with all peers using sta_rec command with
+ * the below exception cases.
+ * - single rate : if a rate is passed along with different preambles,
+ * we select the highest one as fixed rate. i.e VHT MCS for VHT peers.
+ * - 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_queue_work(hw, &dev->rc_work);
+
+ return 0;
+}
+
+static void mt7996_sta_set_4addr(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ bool enabled)
+{
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+
+ if (enabled)
+ set_bit(MT_WCID_FLAG_4ADDR, &msta->wcid.flags);
+ else
+ clear_bit(MT_WCID_FLAG_4ADDR, &msta->wcid.flags);
+
+ mt7996_mcu_wtbl_update_hdr_trans(dev, vif, sta);
+}
+
+static void mt7996_sta_set_decap_offload(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ bool enabled)
+{
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+
+ if (enabled)
+ set_bit(MT_WCID_FLAG_HDR_TRANS, &msta->wcid.flags);
+ else
+ clear_bit(MT_WCID_FLAG_HDR_TRANS, &msta->wcid.flags);
+
+ mt7996_mcu_wtbl_update_hdr_trans(dev, vif, sta);
+}
+
+static const char mt7996_gstrings_stats[][ETH_GSTRING_LEN] = {
+ "tx_ampdu_cnt",
+ "tx_stop_q_empty_cnt",
+ "tx_mpdu_attempts",
+ "tx_mpdu_success",
+ "tx_rwp_fail_cnt",
+ "tx_rwp_need_cnt",
+ "tx_pkt_ebf_cnt",
+ "tx_pkt_ibf_cnt",
+ "tx_ampdu_len:0-1",
+ "tx_ampdu_len:2-10",
+ "tx_ampdu_len:11-19",
+ "tx_ampdu_len:20-28",
+ "tx_ampdu_len:29-37",
+ "tx_ampdu_len:38-46",
+ "tx_ampdu_len:47-55",
+ "tx_ampdu_len:56-79",
+ "tx_ampdu_len:80-103",
+ "tx_ampdu_len:104-127",
+ "tx_ampdu_len:128-151",
+ "tx_ampdu_len:152-175",
+ "tx_ampdu_len:176-199",
+ "tx_ampdu_len:200-223",
+ "tx_ampdu_len:224-247",
+ "ba_miss_count",
+ "tx_beamformer_ppdu_iBF",
+ "tx_beamformer_ppdu_eBF",
+ "tx_beamformer_rx_feedback_all",
+ "tx_beamformer_rx_feedback_he",
+ "tx_beamformer_rx_feedback_vht",
+ "tx_beamformer_rx_feedback_ht",
+ "tx_beamformer_rx_feedback_bw", /* zero based idx: 20, 40, 80, 160 */
+ "tx_beamformer_rx_feedback_nc",
+ "tx_beamformer_rx_feedback_nr",
+ "tx_beamformee_ok_feedback_pkts",
+ "tx_beamformee_feedback_trig",
+ "tx_mu_beamforming",
+ "tx_mu_mpdu",
+ "tx_mu_successful_mpdu",
+ "tx_su_successful_mpdu",
+ "tx_msdu_pack_1",
+ "tx_msdu_pack_2",
+ "tx_msdu_pack_3",
+ "tx_msdu_pack_4",
+ "tx_msdu_pack_5",
+ "tx_msdu_pack_6",
+ "tx_msdu_pack_7",
+ "tx_msdu_pack_8",
+
+ /* rx counters */
+ "rx_fifo_full_cnt",
+ "rx_mpdu_cnt",
+ "channel_idle_cnt",
+ "rx_vector_mismatch_cnt",
+ "rx_delimiter_fail_cnt",
+ "rx_len_mismatch_cnt",
+ "rx_ampdu_cnt",
+ "rx_ampdu_bytes_cnt",
+ "rx_ampdu_valid_subframe_cnt",
+ "rx_ampdu_valid_subframe_b_cnt",
+ "rx_pfdrop_cnt",
+ "rx_vec_queue_overflow_drop_cnt",
+ "rx_ba_cnt",
+
+ /* per vif counters */
+ "v_tx_mode_cck",
+ "v_tx_mode_ofdm",
+ "v_tx_mode_ht",
+ "v_tx_mode_ht_gf",
+ "v_tx_mode_vht",
+ "v_tx_mode_he_su",
+ "v_tx_mode_he_ext_su",
+ "v_tx_mode_he_tb",
+ "v_tx_mode_he_mu",
+ "v_tx_bw_20",
+ "v_tx_bw_40",
+ "v_tx_bw_80",
+ "v_tx_bw_160",
+ "v_tx_mcs_0",
+ "v_tx_mcs_1",
+ "v_tx_mcs_2",
+ "v_tx_mcs_3",
+ "v_tx_mcs_4",
+ "v_tx_mcs_5",
+ "v_tx_mcs_6",
+ "v_tx_mcs_7",
+ "v_tx_mcs_8",
+ "v_tx_mcs_9",
+ "v_tx_mcs_10",
+ "v_tx_mcs_11",
+};
+
+#define MT7996_SSTATS_LEN ARRAY_SIZE(mt7996_gstrings_stats)
+
+/* Ethtool related API */
+static
+void mt7996_get_et_strings(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ u32 sset, u8 *data)
+{
+ if (sset == ETH_SS_STATS)
+ memcpy(data, *mt7996_gstrings_stats,
+ sizeof(mt7996_gstrings_stats));
+}
+
+static
+int mt7996_get_et_sset_count(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, int sset)
+{
+ if (sset == ETH_SS_STATS)
+ return MT7996_SSTATS_LEN;
+
+ return 0;
+}
+
+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;
+
+ if (msta->vif->mt76.idx != wi->idx)
+ return;
+
+ mt76_ethtool_worker(wi, &msta->stats);
+}
+
+static
+void mt7996_get_et_stats(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ethtool_stats *stats, u64 *data)
+{
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt76_ethtool_worker_info wi = {
+ .data = data,
+ .idx = mvif->mt76.idx,
+ };
+ struct mib_stats *mib = &phy->mib;
+ /* See mt7996_ampdu_stat_read_phy, etc */
+ int i, ei = 0;
+
+ mutex_lock(&dev->mt76.mutex);
+
+ mt7996_mac_update_stats(phy);
+
+ data[ei++] = mib->tx_ampdu_cnt;
+ data[ei++] = mib->tx_stop_q_empty_cnt;
+ data[ei++] = mib->tx_mpdu_attempts_cnt;
+ data[ei++] = mib->tx_mpdu_success_cnt;
+ data[ei++] = mib->tx_rwp_fail_cnt;
+ data[ei++] = mib->tx_rwp_need_cnt;
+ data[ei++] = mib->tx_bf_ebf_ppdu_cnt;
+ data[ei++] = mib->tx_bf_ibf_ppdu_cnt;
+
+ /* Tx ampdu stat */
+ for (i = 0; i < 15 /*ARRAY_SIZE(bound)*/; i++)
+ data[ei++] = phy->mt76->aggr_stats[i];
+ data[ei++] = phy->mib.ba_miss_cnt;
+
+ /* Tx Beamformer monitor */
+ data[ei++] = mib->tx_bf_ibf_ppdu_cnt;
+ data[ei++] = mib->tx_bf_ebf_ppdu_cnt;
+
+ /* Tx Beamformer Rx feedback monitor */
+ data[ei++] = mib->tx_bf_rx_fb_all_cnt;
+ data[ei++] = mib->tx_bf_rx_fb_he_cnt;
+ data[ei++] = mib->tx_bf_rx_fb_vht_cnt;
+ data[ei++] = mib->tx_bf_rx_fb_ht_cnt;
+
+ data[ei++] = mib->tx_bf_rx_fb_bw;
+ data[ei++] = mib->tx_bf_rx_fb_nc_cnt;
+ data[ei++] = mib->tx_bf_rx_fb_nr_cnt;
+
+ /* Tx Beamformee Rx NDPA & Tx feedback report */
+ data[ei++] = mib->tx_bf_fb_cpl_cnt;
+ data[ei++] = mib->tx_bf_fb_trig_cnt;
+
+ /* Tx SU & MU counters */
+ data[ei++] = mib->tx_mu_bf_cnt;
+ data[ei++] = mib->tx_mu_mpdu_cnt;
+ data[ei++] = mib->tx_mu_acked_mpdu_cnt;
+ data[ei++] = mib->tx_su_acked_mpdu_cnt;
+
+ /* Tx amsdu info (pack-count histogram) */
+ for (i = 0; i < ARRAY_SIZE(mib->tx_amsdu); i++)
+ data[ei++] = mib->tx_amsdu[i];
+
+ /* rx counters */
+ data[ei++] = mib->rx_fifo_full_cnt;
+ data[ei++] = mib->rx_mpdu_cnt;
+ data[ei++] = mib->channel_idle_cnt;
+ data[ei++] = mib->rx_vector_mismatch_cnt;
+ data[ei++] = mib->rx_delimiter_fail_cnt;
+ data[ei++] = mib->rx_len_mismatch_cnt;
+ data[ei++] = mib->rx_ampdu_cnt;
+ data[ei++] = mib->rx_ampdu_bytes_cnt;
+ data[ei++] = mib->rx_ampdu_valid_subframe_cnt;
+ data[ei++] = mib->rx_ampdu_valid_subframe_bytes_cnt;
+ data[ei++] = mib->rx_pfdrop_cnt;
+ data[ei++] = mib->rx_vec_queue_overflow_drop_cnt;
+ data[ei++] = mib->rx_ba_cnt;
+
+ /* Add values for all stations owned by this vif */
+ wi.initial_stat_idx = ei;
+ ieee80211_iterate_stations_atomic(hw, mt7996_ethtool_worker, &wi);
+
+ mutex_unlock(&dev->mt76.mutex);
+
+ if (wi.sta_count == 0)
+ return;
+
+ ei += wi.worker_stat_count;
+ if (ei != MT7996_SSTATS_LEN)
+ dev_err(dev->mt76.dev, "ei: %d MT7996_SSTATS_LEN: %d",
+ ei, (int)MT7996_SSTATS_LEN);
+}
+
+static void
+mt7996_twt_teardown_request(struct ieee80211_hw *hw,
+ struct ieee80211_sta *sta,
+ u8 flowid)
+{
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+
+ mutex_lock(&dev->mt76.mutex);
+ mt7996_mac_twt_teardown_flow(dev, msta, flowid);
+ mutex_unlock(&dev->mt76.mutex);
+}
+
+static int
+mt7996_set_radar_background(struct ieee80211_hw *hw,
+ struct cfg80211_chan_def *chandef)
+{
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ struct mt7996_dev *dev = phy->dev;
+ int ret = -EINVAL;
+ bool running;
+
+ mutex_lock(&dev->mt76.mutex);
+
+ if (dev->mt76.region == NL80211_DFS_UNSET)
+ goto out;
+
+ if (dev->rdd2_phy && dev->rdd2_phy != phy) {
+ /* rdd2 is already locked */
+ ret = -EBUSY;
+ goto out;
+ }
+
+ /* rdd2 already configured on a radar channel */
+ running = dev->rdd2_phy &&
+ cfg80211_chandef_valid(&dev->rdd2_chandef) &&
+ !!(dev->rdd2_chandef.chan->flags & IEEE80211_CHAN_RADAR);
+
+ if (!chandef || running ||
+ !(chandef->chan->flags & IEEE80211_CHAN_RADAR)) {
+ ret = mt7996_mcu_rdd_background_enable(phy, NULL);
+ if (ret)
+ goto out;
+
+ if (!running)
+ goto update_phy;
+ }
+
+ ret = mt7996_mcu_rdd_background_enable(phy, chandef);
+ if (ret)
+ goto out;
+
+update_phy:
+ dev->rdd2_phy = chandef ? phy : NULL;
+ if (chandef)
+ dev->rdd2_chandef = *chandef;
+out:
+ mutex_unlock(&dev->mt76.mutex);
+
+ return ret;
+}
+
+const struct ieee80211_ops mt7996_ops = {
+ .tx = mt7996_tx,
+ .start = mt7996_start,
+ .stop = mt7996_stop,
+ .add_interface = mt7996_add_interface,
+ .remove_interface = mt7996_remove_interface,
+ .config = mt7996_config,
+ .conf_tx = mt7996_conf_tx,
+ .configure_filter = mt7996_configure_filter,
+ .bss_info_changed = mt7996_bss_info_changed,
+ .sta_add = mt7996_sta_add,
+ .sta_remove = mt7996_sta_remove,
+ .sta_pre_rcu_remove = mt76_sta_pre_rcu_remove,
+ .sta_rc_update = mt7996_sta_rc_update,
+ .set_key = mt7996_set_key,
+ .ampdu_action = mt7996_ampdu_action,
+ .set_rts_threshold = mt7996_set_rts_threshold,
+ .wake_tx_queue = mt76_wake_tx_queue,
+ .sw_scan_start = mt76_sw_scan,
+ .sw_scan_complete = mt76_sw_scan_complete,
+ .release_buffered_frames = mt76_release_buffered_frames,
+ .get_txpower = mt76_get_txpower,
+ .channel_switch_beacon = mt7996_channel_switch_beacon,
+ .get_stats = mt7996_get_stats,
+ .get_et_sset_count = mt7996_get_et_sset_count,
+ .get_et_stats = mt7996_get_et_stats,
+ .get_et_strings = mt7996_get_et_strings,
+ .get_tsf = mt7996_get_tsf,
+ .set_tsf = mt7996_set_tsf,
+ .offset_tsf = mt7996_offset_tsf,
+ .get_survey = mt76_get_survey,
+ .get_antenna = mt76_get_antenna,
+ .set_antenna = mt7996_set_antenna,
+ .set_bitrate_mask = mt7996_set_bitrate_mask,
+ .set_coverage_class = mt7996_set_coverage_class,
+ .sta_statistics = mt7996_sta_statistics,
+ .sta_set_4addr = mt7996_sta_set_4addr,
+ .sta_set_decap_offload = mt7996_sta_set_decap_offload,
+ .add_twt_setup = mt7996_mac_add_twt_setup,
+ .twt_teardown_request = mt7996_twt_teardown_request,
+#ifdef CONFIG_MAC80211_DEBUGFS
+ .sta_add_debugfs = mt7996_sta_add_debugfs,
+#endif
+ .set_radar_background = mt7996_set_radar_background,
+};
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
new file mode 100644
index 000000000000..04e1d10bbd21
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
@@ -0,0 +1,3607 @@
+// SPDX-License-Identifier: ISC
+/*
+ * Copyright (C) 2022 MediaTek Inc.
+ */
+
+#include <linux/firmware.h>
+#include <linux/fs.h>
+#include "mt7996.h"
+#include "mcu.h"
+#include "mac.h"
+#include "eeprom.h"
+
+struct mt7996_patch_hdr {
+ char build_date[16];
+ char platform[4];
+ __be32 hw_sw_ver;
+ __be32 patch_ver;
+ __be16 checksum;
+ u16 reserved;
+ struct {
+ __be32 patch_ver;
+ __be32 subsys;
+ __be32 feature;
+ __be32 n_region;
+ __be32 crc;
+ u32 reserved[11];
+ } desc;
+} __packed;
+
+struct mt7996_patch_sec {
+ __be32 type;
+ __be32 offs;
+ __be32 size;
+ union {
+ __be32 spec[13];
+ struct {
+ __be32 addr;
+ __be32 len;
+ __be32 sec_key_idx;
+ __be32 align_len;
+ u32 reserved[9];
+ } info;
+ };
+} __packed;
+
+struct mt7996_fw_trailer {
+ u8 chip_id;
+ u8 eco_code;
+ u8 n_region;
+ u8 format_ver;
+ u8 format_flag;
+ u8 reserved[2];
+ char fw_ver[10];
+ char build_date[15];
+ u32 crc;
+} __packed;
+
+struct mt7996_fw_region {
+ __le32 decomp_crc;
+ __le32 decomp_len;
+ __le32 decomp_blk_sz;
+ u8 reserved[4];
+ __le32 addr;
+ __le32 len;
+ u8 feature_set;
+ u8 reserved1[15];
+} __packed;
+
+#define MCU_PATCH_ADDRESS 0x200000
+
+#define HE_PHY(p, c) u8_get_bits(c, IEEE80211_HE_PHY_##p)
+#define HE_MAC(m, c) u8_get_bits(c, IEEE80211_HE_MAC_##m)
+
+static bool sr_scene_detect = true;
+module_param(sr_scene_detect, bool, 0644);
+MODULE_PARM_DESC(sr_scene_detect, "Enable firmware scene detection algorithm");
+
+static u8
+mt7996_mcu_get_sta_nss(u16 mcs_map)
+{
+ u8 nss;
+
+ for (nss = 8; nss > 0; nss--) {
+ u8 nss_mcs = (mcs_map >> (2 * (nss - 1))) & 3;
+
+ if (nss_mcs != IEEE80211_VHT_MCS_NOT_SUPPORTED)
+ break;
+ }
+
+ return nss - 1;
+}
+
+static void
+mt7996_mcu_set_sta_he_mcs(struct ieee80211_sta *sta, __le16 *he_mcs,
+ u16 mcs_map)
+{
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ enum nl80211_band band = msta->vif->phy->mt76->chandef.chan->band;
+ const u16 *mask = msta->vif->bitrate_mask.control[band].he_mcs;
+ int nss, max_nss = sta->deflink.rx_nss > 3 ? 4 : sta->deflink.rx_nss;
+
+ for (nss = 0; nss < max_nss; nss++) {
+ int mcs;
+
+ switch ((mcs_map >> (2 * nss)) & 0x3) {
+ case IEEE80211_HE_MCS_SUPPORT_0_11:
+ mcs = GENMASK(11, 0);
+ break;
+ case IEEE80211_HE_MCS_SUPPORT_0_9:
+ mcs = GENMASK(9, 0);
+ break;
+ case IEEE80211_HE_MCS_SUPPORT_0_7:
+ mcs = GENMASK(7, 0);
+ break;
+ default:
+ mcs = 0;
+ }
+
+ mcs = mcs ? fls(mcs & mask[nss]) - 1 : -1;
+
+ switch (mcs) {
+ case 0 ... 7:
+ mcs = IEEE80211_HE_MCS_SUPPORT_0_7;
+ break;
+ case 8 ... 9:
+ mcs = IEEE80211_HE_MCS_SUPPORT_0_9;
+ break;
+ case 10 ... 11:
+ mcs = IEEE80211_HE_MCS_SUPPORT_0_11;
+ break;
+ default:
+ mcs = IEEE80211_HE_MCS_NOT_SUPPORTED;
+ break;
+ }
+ mcs_map &= ~(0x3 << (nss * 2));
+ mcs_map |= mcs << (nss * 2);
+ }
+
+ *he_mcs = cpu_to_le16(mcs_map);
+}
+
+static void
+mt7996_mcu_set_sta_vht_mcs(struct ieee80211_sta *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;
+
+ for (nss = 0; nss < max_nss; nss++, mcs_map >>= 2) {
+ switch (mcs_map & 0x3) {
+ case IEEE80211_VHT_MCS_SUPPORT_0_9:
+ mcs = GENMASK(9, 0);
+ break;
+ case IEEE80211_VHT_MCS_SUPPORT_0_8:
+ mcs = GENMASK(8, 0);
+ break;
+ case IEEE80211_VHT_MCS_SUPPORT_0_7:
+ mcs = GENMASK(7, 0);
+ break;
+ default:
+ mcs = 0;
+ }
+
+ vht_mcs[nss] = cpu_to_le16(mcs & mask[nss]);
+ }
+}
+
+static void
+mt7996_mcu_set_sta_ht_mcs(struct ieee80211_sta *sta, u8 *ht_mcs,
+ const u8 *mask)
+{
+ int nss, max_nss = sta->deflink.rx_nss > 3 ? 4 : sta->deflink.rx_nss;
+
+ for (nss = 0; nss < max_nss; nss++)
+ ht_mcs[nss] = sta->deflink.ht_cap.mcs.rx_mask[nss] & mask[nss];
+}
+
+static int
+mt7996_mcu_parse_response(struct mt76_dev *mdev, int cmd,
+ struct sk_buff *skb, int seq)
+{
+ struct mt7996_mcu_rxd *rxd;
+ struct mt7996_mcu_uni_event *event;
+ int mcu_cmd = FIELD_GET(__MCU_CMD_FIELD_ID, cmd);
+ int ret = 0;
+
+ if (!skb) {
+ dev_err(mdev->dev, "Message %08x (seq %d) timeout\n",
+ cmd, seq);
+ return -ETIMEDOUT;
+ }
+
+ rxd = (struct mt7996_mcu_rxd *)skb->data;
+ if (seq != rxd->seq)
+ return -EAGAIN;
+
+ if (cmd == MCU_CMD(PATCH_SEM_CONTROL)) {
+ skb_pull(skb, sizeof(*rxd) - 4);
+ ret = *skb->data;
+ } else if ((rxd->option & MCU_UNI_CMD_EVENT) &&
+ rxd->eid == MCU_UNI_EVENT_RESULT) {
+ skb_pull(skb, sizeof(*rxd));
+ event = (struct mt7996_mcu_uni_event *)skb->data;
+ ret = le32_to_cpu(event->status);
+ /* skip invalid event */
+ if (mcu_cmd != event->cid)
+ ret = -EAGAIN;
+ } else {
+ skb_pull(skb, sizeof(struct mt7996_mcu_rxd));
+ }
+
+ return ret;
+}
+
+static int
+mt7996_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb,
+ int cmd, int *wait_seq)
+{
+ struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
+ int txd_len, mcu_cmd = FIELD_GET(__MCU_CMD_FIELD_ID, cmd);
+ struct mt76_connac2_mcu_uni_txd *uni_txd;
+ struct mt76_connac2_mcu_txd *mcu_txd;
+ enum mt76_mcuq_id qid;
+ __le32 *txd;
+ u32 val;
+ u8 seq;
+
+ mdev->mcu.timeout = 20 * HZ;
+
+ seq = ++dev->mt76.mcu.msg_seq & 0xf;
+ if (!seq)
+ seq = ++dev->mt76.mcu.msg_seq & 0xf;
+
+ if (cmd == MCU_CMD(FW_SCATTER)) {
+ qid = MT_MCUQ_FWDL;
+ goto exit;
+ }
+
+ 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))
+ qid = MT_MCUQ_WA;
+ else
+ qid = MT_MCUQ_WM;
+
+ val = FIELD_PREP(MT_TXD0_TX_BYTES, skb->len) |
+ FIELD_PREP(MT_TXD0_PKT_FMT, MT_TX_TYPE_CMD) |
+ FIELD_PREP(MT_TXD0_Q_IDX, MT_TX_MCU_PORT_RX_Q0);
+ txd[0] = cpu_to_le32(val);
+
+ val = FIELD_PREP(MT_TXD1_HDR_FORMAT, MT_HDR_FORMAT_CMD);
+ txd[1] = cpu_to_le32(val);
+
+ if (cmd & __MCU_CMD_FIELD_UNI) {
+ uni_txd = (struct mt76_connac2_mcu_uni_txd *)txd;
+ uni_txd->len = cpu_to_le16(skb->len - sizeof(uni_txd->txd));
+ uni_txd->cid = cpu_to_le16(mcu_cmd);
+ uni_txd->s2d_index = MCU_S2D_H2CN;
+ uni_txd->pkt_type = MCU_PKT_ID;
+ uni_txd->seq = seq;
+
+ if (cmd & __MCU_CMD_FIELD_QUERY)
+ uni_txd->option = MCU_CMD_UNI_QUERY_ACK;
+ else
+ uni_txd->option = MCU_CMD_UNI_EXT_ACK;
+
+ if ((cmd & __MCU_CMD_FIELD_WA) && (cmd & __MCU_CMD_FIELD_WM))
+ uni_txd->s2d_index = MCU_S2D_H2CN;
+ else if (cmd & __MCU_CMD_FIELD_WA)
+ uni_txd->s2d_index = MCU_S2D_H2C;
+ else if (cmd & __MCU_CMD_FIELD_WM)
+ uni_txd->s2d_index = MCU_S2D_H2N;
+
+ goto exit;
+ }
+
+ mcu_txd = (struct mt76_connac2_mcu_txd *)txd;
+ mcu_txd->len = cpu_to_le16(skb->len - sizeof(mcu_txd->txd));
+ mcu_txd->pq_id = cpu_to_le16(MCU_PQ_ID(MT_TX_PORT_IDX_MCU,
+ MT_TX_MCU_PORT_RX_Q0));
+ mcu_txd->pkt_type = MCU_PKT_ID;
+ mcu_txd->seq = seq;
+
+ mcu_txd->cid = FIELD_GET(__MCU_CMD_FIELD_ID, cmd);
+ mcu_txd->set_query = MCU_Q_NA;
+ mcu_txd->ext_cid = FIELD_GET(__MCU_CMD_FIELD_EXT_ID, cmd);
+ if (mcu_txd->ext_cid) {
+ mcu_txd->ext_cid_ack = 1;
+
+ if (cmd & __MCU_CMD_FIELD_QUERY)
+ mcu_txd->set_query = MCU_Q_QUERY;
+ else
+ mcu_txd->set_query = MCU_Q_SET;
+ }
+
+ if (cmd & __MCU_CMD_FIELD_WA)
+ mcu_txd->s2d_index = MCU_S2D_H2C;
+ else
+ mcu_txd->s2d_index = MCU_S2D_H2N;
+
+exit:
+ if (wait_seq)
+ *wait_seq = seq;
+
+ return mt76_tx_queue_skb_raw(dev, mdev->q_mcu[qid], skb, 0);
+}
+
+int mt7996_mcu_wa_cmd(struct mt7996_dev *dev, int cmd, u32 a1, u32 a2, u32 a3)
+{
+ struct {
+ __le32 args[3];
+ } req = {
+ .args = {
+ cpu_to_le32(a1),
+ cpu_to_le32(a2),
+ cpu_to_le32(a3),
+ },
+ };
+
+ return mt76_mcu_send_msg(&dev->mt76, cmd, &req, sizeof(req), false);
+}
+
+static void
+mt7996_mcu_csa_finish(void *priv, u8 *mac, struct ieee80211_vif *vif)
+{
+ if (vif->bss_conf.csa_active)
+ ieee80211_csa_finish(vif);
+}
+
+static void
+mt7996_mcu_rx_radar_detected(struct mt7996_dev *dev, struct sk_buff *skb)
+{
+ struct mt76_phy *mphy = &dev->mt76.phy;
+ struct mt7996_mcu_rdd_report *r;
+
+ r = (struct mt7996_mcu_rdd_report *)skb->data;
+
+ mphy = dev->mt76.phys[r->band_idx];
+ if (!mphy)
+ return;
+
+ if (r->band_idx == MT_RX_SEL2)
+ cfg80211_background_radar_event(mphy->hw->wiphy,
+ &dev->rdd2_chandef,
+ GFP_ATOMIC);
+ else
+ ieee80211_radar_detected(mphy->hw);
+ dev->hw_pattern++;
+}
+
+static void
+mt7996_mcu_rx_log_message(struct mt7996_dev *dev, struct sk_buff *skb)
+{
+#define UNI_EVENT_FW_LOG_FORMAT 0
+ struct mt7996_mcu_rxd *rxd = (struct mt7996_mcu_rxd *)skb->data;
+ const char *data = (char *)&rxd[1] + 4, *type;
+ struct tlv *tlv = (struct tlv *)data;
+ int len;
+
+ if (!(rxd->option & MCU_UNI_CMD_EVENT)) {
+ len = skb->len - sizeof(*rxd);
+ data = (char *)&rxd[1];
+ goto out;
+ }
+
+ if (le16_to_cpu(tlv->tag) != UNI_EVENT_FW_LOG_FORMAT)
+ return;
+
+ data += sizeof(*tlv) + 4;
+ len = le16_to_cpu(tlv->len) - sizeof(*tlv) - 4;
+
+out:
+ switch (rxd->s2d_index) {
+ case 0:
+ if (mt7996_debugfs_rx_log(dev, data, len))
+ return;
+
+ type = "WM";
+ break;
+ case 2:
+ type = "WA";
+ break;
+ default:
+ type = "unknown";
+ break;
+ }
+
+ wiphy_info(mt76_hw(dev)->wiphy, "%s: %.*s", type, len, data);
+}
+
+static void
+mt7996_mcu_cca_finish(void *priv, u8 *mac, struct ieee80211_vif *vif)
+{
+ if (!vif->bss_conf.color_change_active)
+ return;
+
+ ieee80211_color_change_finish(vif);
+}
+
+static void
+mt7996_mcu_ie_countdown(struct mt7996_dev *dev, struct sk_buff *skb)
+{
+#define UNI_EVENT_IE_COUNTDOWN_CSA 0
+#define UNI_EVENT_IE_COUNTDOWN_BCC 1
+ struct header {
+ u8 band;
+ u8 rsv[3];
+ };
+ struct mt76_phy *mphy = &dev->mt76.phy;
+ struct mt7996_mcu_rxd *rxd = (struct mt7996_mcu_rxd *)skb->data;
+ const char *data = (char *)&rxd[1], *tail;
+ struct header *hdr = (struct header *)data;
+ struct tlv *tlv = (struct tlv *)(data + 4);
+
+ if (hdr->band && dev->mt76.phys[hdr->band])
+ mphy = dev->mt76.phys[hdr->band];
+
+ tail = skb->data + le16_to_cpu(rxd->len);
+ while (data + sizeof(struct tlv) < tail && le16_to_cpu(tlv->len)) {
+ switch (le16_to_cpu(tlv->tag)) {
+ case UNI_EVENT_IE_COUNTDOWN_CSA:
+ ieee80211_iterate_active_interfaces_atomic(mphy->hw,
+ IEEE80211_IFACE_ITER_RESUME_ALL,
+ mt7996_mcu_csa_finish, mphy->hw);
+ break;
+ case UNI_EVENT_IE_COUNTDOWN_BCC:
+ ieee80211_iterate_active_interfaces_atomic(mphy->hw,
+ IEEE80211_IFACE_ITER_RESUME_ALL,
+ mt7996_mcu_cca_finish, mphy->hw);
+ break;
+ }
+
+ data += le16_to_cpu(tlv->len);
+ tlv = (struct tlv *)data;
+ }
+}
+
+static void
+mt7996_mcu_rx_ext_event(struct mt7996_dev *dev, struct sk_buff *skb)
+{
+ struct mt7996_mcu_rxd *rxd = (struct mt7996_mcu_rxd *)skb->data;
+
+ switch (rxd->ext_eid) {
+ case MCU_EXT_EVENT_FW_LOG_2_HOST:
+ mt7996_mcu_rx_log_message(dev, skb);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+mt7996_mcu_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb)
+{
+ struct mt7996_mcu_rxd *rxd = (struct mt7996_mcu_rxd *)skb->data;
+
+ switch (rxd->eid) {
+ case MCU_EVENT_EXT:
+ mt7996_mcu_rx_ext_event(dev, skb);
+ break;
+ default:
+ break;
+ }
+ dev_kfree_skb(skb);
+}
+
+static void
+mt7996_mcu_uni_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb)
+{
+ struct mt7996_mcu_rxd *rxd = (struct mt7996_mcu_rxd *)skb->data;
+
+ switch (rxd->eid) {
+ case MCU_UNI_EVENT_FW_LOG_2_HOST:
+ mt7996_mcu_rx_log_message(dev, skb);
+ break;
+ case MCU_UNI_EVENT_IE_COUNTDOWN:
+ mt7996_mcu_ie_countdown(dev, skb);
+ break;
+ case MCU_UNI_EVENT_RDD_REPORT:
+ mt7996_mcu_rx_radar_detected(dev, skb);
+ break;
+ default:
+ break;
+ }
+ dev_kfree_skb(skb);
+}
+
+void mt7996_mcu_rx_event(struct mt7996_dev *dev, struct sk_buff *skb)
+{
+ struct mt7996_mcu_rxd *rxd = (struct mt7996_mcu_rxd *)skb->data;
+
+ if (rxd->option & MCU_UNI_CMD_UNSOLICITED_EVENT) {
+ mt7996_mcu_uni_rx_unsolicited_event(dev, skb);
+ return;
+ }
+
+ /* WA still uses legacy event*/
+ if (rxd->ext_eid == MCU_EXT_EVENT_FW_LOG_2_HOST ||
+ !rxd->seq)
+ mt7996_mcu_rx_unsolicited_event(dev, skb);
+ else
+ mt76_mcu_rx_event(&dev->mt76, skb);
+}
+
+static struct tlv *
+mt7996_mcu_add_uni_tlv(struct sk_buff *skb, u16 tag, u16 len)
+{
+ struct tlv *ptlv, tlv = {
+ .tag = cpu_to_le16(tag),
+ .len = cpu_to_le16(len),
+ };
+
+ ptlv = skb_put(skb, len);
+ memcpy(ptlv, &tlv, sizeof(tlv));
+
+ return ptlv;
+}
+
+static void
+mt7996_mcu_bss_rfch_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
+ struct mt7996_phy *phy)
+{
+ static const u8 rlm_ch_band[] = {
+ [NL80211_BAND_2GHZ] = 1,
+ [NL80211_BAND_5GHZ] = 2,
+ [NL80211_BAND_6GHZ] = 3,
+ };
+ struct cfg80211_chan_def *chandef = &phy->mt76->chandef;
+ struct bss_rlm_tlv *ch;
+ struct tlv *tlv;
+ int freq1 = chandef->center_freq1;
+
+ tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_RLM, sizeof(*ch));
+
+ ch = (struct bss_rlm_tlv *)tlv;
+ ch->control_channel = chandef->chan->hw_value;
+ ch->center_chan = ieee80211_frequency_to_channel(freq1);
+ ch->bw = mt76_connac_chan_bw(chandef);
+ ch->tx_streams = hweight8(phy->mt76->antenna_mask);
+ ch->rx_streams = hweight8(phy->mt76->antenna_mask);
+ ch->band = rlm_ch_band[chandef->chan->band];
+
+ if (chandef->width == NL80211_CHAN_WIDTH_80P80) {
+ int freq2 = chandef->center_freq2;
+
+ ch->center_chan2 = ieee80211_frequency_to_channel(freq2);
+ }
+}
+
+static void
+mt7996_mcu_bss_ra_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
+ struct mt7996_phy *phy)
+{
+ struct bss_ra_tlv *ra;
+ struct tlv *tlv;
+
+ tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_RA, sizeof(*ra));
+
+ ra = (struct bss_ra_tlv *)tlv;
+ ra->short_preamble = true;
+}
+
+static void
+mt7996_mcu_bss_he_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
+ struct mt7996_phy *phy)
+{
+#define DEFAULT_HE_PE_DURATION 4
+#define DEFAULT_HE_DURATION_RTS_THRES 1023
+ const struct ieee80211_sta_he_cap *cap;
+ struct bss_info_uni_he *he;
+ struct tlv *tlv;
+
+ cap = mt76_connac_get_he_phy_cap(phy->mt76, vif);
+
+ tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_HE_BASIC, sizeof(*he));
+
+ he = (struct bss_info_uni_he *)tlv;
+ he->he_pe_duration = vif->bss_conf.htc_trig_based_pkt_ext;
+ if (!he->he_pe_duration)
+ he->he_pe_duration = DEFAULT_HE_PE_DURATION;
+
+ he->he_rts_thres = cpu_to_le16(vif->bss_conf.frame_time_rts_th);
+ if (!he->he_rts_thres)
+ he->he_rts_thres = cpu_to_le16(DEFAULT_HE_DURATION_RTS_THRES);
+
+ he->max_nss_mcs[CMD_HE_MCS_BW80] = cap->he_mcs_nss_supp.tx_mcs_80;
+ he->max_nss_mcs[CMD_HE_MCS_BW160] = cap->he_mcs_nss_supp.tx_mcs_160;
+ he->max_nss_mcs[CMD_HE_MCS_BW8080] = cap->he_mcs_nss_supp.tx_mcs_80p80;
+}
+
+static void
+mt7996_mcu_bss_bmc_tlv(struct sk_buff *skb, struct mt7996_phy *phy)
+{
+ struct bss_rate_tlv *bmc;
+ struct cfg80211_chan_def *chandef = &phy->mt76->chandef;
+ enum nl80211_band band = chandef->chan->band;
+ struct tlv *tlv;
+
+ tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_RATE, sizeof(*bmc));
+
+ bmc = (struct bss_rate_tlv *)tlv;
+ if (band == NL80211_BAND_2GHZ) {
+ bmc->short_preamble = true;
+ } else {
+ bmc->bc_trans = cpu_to_le16(0x8080);
+ bmc->mc_trans = cpu_to_le16(0x8080);
+ bmc->bc_fixed_rate = 1;
+ bmc->mc_fixed_rate = 1;
+ bmc->short_preamble = 1;
+ }
+}
+
+static void
+mt7996_mcu_bss_txcmd_tlv(struct sk_buff *skb, bool en)
+{
+ struct bss_txcmd_tlv *txcmd;
+ struct tlv *tlv;
+
+ tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_TXCMD, sizeof(*txcmd));
+
+ txcmd = (struct bss_txcmd_tlv *)tlv;
+ txcmd->txcmd_mode = en;
+}
+
+static void
+mt7996_mcu_bss_mld_tlv(struct sk_buff *skb, struct ieee80211_vif *vif)
+{
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct bss_mld_tlv *mld;
+ struct tlv *tlv;
+
+ tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_MLD, sizeof(*mld));
+
+ mld = (struct bss_mld_tlv *)tlv;
+ mld->group_mld_id = 0xff;
+ mld->own_mld_id = mvif->mt76.idx;
+ mld->remap_idx = 0xff;
+}
+
+static void
+mt7996_mcu_bss_sec_tlv(struct sk_buff *skb, struct ieee80211_vif *vif)
+{
+ struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
+ struct bss_sec_tlv *sec;
+ struct tlv *tlv;
+
+ tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_SEC, sizeof(*sec));
+
+ sec = (struct bss_sec_tlv *)tlv;
+ sec->cipher = mvif->cipher;
+}
+
+static int
+mt7996_mcu_muar_config(struct mt7996_phy *phy, struct ieee80211_vif *vif,
+ bool bssid, bool enable)
+{
+#define UNI_MUAR_ENTRY 2
+ struct mt7996_dev *dev = phy->dev;
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ u32 idx = mvif->mt76.omac_idx - REPEATER_BSSID_START;
+ const u8 *addr = vif->addr;
+
+ struct {
+ struct {
+ u8 band;
+ u8 __rsv[3];
+ } hdr;
+
+ __le16 tag;
+ __le16 len;
+
+ bool smesh;
+ u8 bssid;
+ u8 index;
+ u8 entry_add;
+ u8 addr[ETH_ALEN];
+ u8 __rsv[2];
+ } __packed req = {
+ .hdr.band = phy->mt76->band_idx,
+ .tag = cpu_to_le16(UNI_MUAR_ENTRY),
+ .len = cpu_to_le16(sizeof(req) - sizeof(req.hdr)),
+ .smesh = false,
+ .index = idx * 2 + bssid,
+ .entry_add = true,
+ };
+
+ if (bssid)
+ addr = vif->bss_conf.bssid;
+
+ if (enable)
+ memcpy(req.addr, addr, ETH_ALEN);
+
+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(REPT_MUAR), &req,
+ sizeof(req), true);
+}
+
+static int
+mt7996_mcu_bss_basic_tlv(struct sk_buff *skb,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct mt76_phy *phy, u16 wlan_idx,
+ bool enable)
+{
+ struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
+ struct cfg80211_chan_def *chandef = &phy->chandef;
+ struct mt76_connac_bss_basic_tlv *bss;
+ u32 type = CONNECTION_INFRA_AP;
+ struct tlv *tlv;
+ int idx;
+
+ switch (vif->type) {
+ case NL80211_IFTYPE_MESH_POINT:
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_MONITOR:
+ break;
+ case NL80211_IFTYPE_STATION:
+ if (enable) {
+ rcu_read_lock();
+ if (!sta)
+ sta = ieee80211_find_sta(vif,
+ vif->bss_conf.bssid);
+ /* TODO: enable BSS_INFO_UAPSD & BSS_INFO_PM */
+ if (sta) {
+ struct mt76_wcid *wcid;
+
+ wcid = (struct mt76_wcid *)sta->drv_priv;
+ wlan_idx = wcid->idx;
+ }
+ rcu_read_unlock();
+ }
+ type = CONNECTION_INFRA_STA;
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ type = CONNECTION_IBSS_ADHOC;
+ break;
+ default:
+ WARN_ON(1);
+ break;
+ }
+
+ tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_BASIC, sizeof(*bss));
+
+ bss = (struct mt76_connac_bss_basic_tlv *)tlv;
+ bss->bcn_interval = cpu_to_le16(vif->bss_conf.beacon_int);
+ bss->dtim_period = vif->bss_conf.dtim_period;
+ bss->bmc_tx_wlan_idx = cpu_to_le16(wlan_idx);
+ bss->sta_idx = cpu_to_le16(wlan_idx);
+ bss->conn_type = cpu_to_le32(type);
+ bss->omac_idx = mvif->omac_idx;
+ bss->band_idx = mvif->band_idx;
+ bss->wmm_idx = mvif->wmm_idx;
+ bss->conn_state = !enable;
+ bss->active = enable;
+
+ idx = mvif->omac_idx > EXT_BSSID_START ? HW_BSSID_0 : mvif->omac_idx;
+ bss->hw_bss_idx = idx;
+
+ if (vif->type == NL80211_IFTYPE_MONITOR) {
+ memcpy(bss->bssid, phy->macaddr, ETH_ALEN);
+ return 0;
+ }
+
+ memcpy(bss->bssid, vif->bss_conf.bssid, ETH_ALEN);
+ bss->bcn_interval = cpu_to_le16(vif->bss_conf.beacon_int);
+ bss->dtim_period = vif->bss_conf.dtim_period;
+ bss->phymode = mt76_connac_get_phy_mode(phy, vif,
+ chandef->chan->band, NULL);
+
+ if (chandef->chan->band == NL80211_BAND_6GHZ)
+ bss->phymode_ext |= PHY_MODE_AX_6G;
+
+ return 0;
+}
+
+static struct sk_buff *
+__mt7996_mcu_alloc_bss_req(struct mt76_dev *dev, struct mt76_vif *mvif, int len)
+{
+ struct bss_req_hdr hdr = {
+ .bss_idx = mvif->idx,
+ };
+ struct sk_buff *skb;
+
+ skb = mt76_mcu_msg_alloc(dev, NULL, len);
+ if (!skb)
+ return ERR_PTR(-ENOMEM);
+
+ skb_put_data(skb, &hdr, sizeof(hdr));
+
+ return skb;
+}
+
+int mt7996_mcu_add_bss_info(struct mt7996_phy *phy,
+ struct ieee80211_vif *vif, int enable)
+{
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_dev *dev = phy->dev;
+ struct sk_buff *skb;
+
+ if (mvif->mt76.omac_idx >= REPEATER_BSSID_START) {
+ mt7996_mcu_muar_config(phy, vif, false, enable);
+ mt7996_mcu_muar_config(phy, vif, true, enable);
+ }
+
+ skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mvif->mt76,
+ MT7996_BSS_UPDATE_MAX_SIZE);
+ if (IS_ERR(skb))
+ return PTR_ERR(skb);
+
+ /* bss_basic must be first */
+ mt7996_mcu_bss_basic_tlv(skb, vif, NULL, phy->mt76,
+ mvif->sta.wcid.idx, enable);
+ mt7996_mcu_bss_sec_tlv(skb, vif);
+
+ if (vif->type == NL80211_IFTYPE_MONITOR)
+ goto out;
+
+ if (enable) {
+ mt7996_mcu_bss_rfch_tlv(skb, vif, phy);
+ mt7996_mcu_bss_bmc_tlv(skb, phy);
+ mt7996_mcu_bss_ra_tlv(skb, vif, phy);
+ mt7996_mcu_bss_txcmd_tlv(skb, true);
+
+ if (vif->bss_conf.he_support)
+ mt7996_mcu_bss_he_tlv(skb, vif, phy);
+
+ /* this tag is necessary no matter if the vif is MLD */
+ mt7996_mcu_bss_mld_tlv(skb, vif);
+ }
+out:
+ return mt76_mcu_skb_send_msg(&dev->mt76, skb,
+ MCU_WMWA_UNI_CMD(BSS_INFO_UPDATE), true);
+}
+
+static int
+mt7996_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif *mvif,
+ 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;
+
+ skb = __mt76_connac_mcu_alloc_sta_req(dev, mvif, wcid,
+ MT7996_STA_UPDATE_MAX_SIZE);
+ if (IS_ERR(skb))
+ return PTR_ERR(skb);
+
+ tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_BA, sizeof(*ba));
+
+ ba = (struct sta_rec_ba_uni *)tlv;
+ ba->ba_type = tx ? MT_BA_TYPE_ORIGINATOR : MT_BA_TYPE_RECIPIENT;
+ ba->winsize = cpu_to_le16(params->buf_size);
+ ba->ssn = cpu_to_le16(params->ssn);
+ ba->ba_en = enable << params->tid;
+ ba->amsdu = params->amsdu;
+ ba->tid = params->tid;
+
+ return mt76_mcu_skb_send_msg(dev, skb,
+ MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
+}
+
+/** starec & wtbl **/
+int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev,
+ struct ieee80211_ampdu_params *params,
+ 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;
+
+ return mt7996_mcu_sta_ba(&dev->mt76, &mvif->mt76, params,
+ enable, true);
+}
+
+int mt7996_mcu_add_rx_ba(struct mt7996_dev *dev,
+ struct ieee80211_ampdu_params *params,
+ 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->mt76, &mvif->mt76, params,
+ enable, false);
+}
+
+static void
+mt7996_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
+{
+ struct ieee80211_he_cap_elem *elem = &sta->deflink.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)
+ return;
+
+ tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HE_V2, sizeof(*he));
+
+ he = (struct sta_rec_he_v2 *)tlv;
+ for (i = 0; i < 11; i++) {
+ if (i < 6)
+ he->he_mac_cap[i] = cpu_to_le16(elem->mac_cap_info[i]);
+ he->he_phy_cap[i] = cpu_to_le16(elem->phy_cap_info[i]);
+ }
+
+ mcs_map = sta->deflink.he_cap.he_mcs_nss_supp;
+ switch (sta->deflink.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,
+ &he->max_nss_mcs[CMD_HE_MCS_BW8080],
+ le16_to_cpu(mcs_map.rx_mcs_80p80));
+
+ mt7996_mcu_set_sta_he_mcs(sta,
+ &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,
+ &he->max_nss_mcs[CMD_HE_MCS_BW80],
+ le16_to_cpu(mcs_map.rx_mcs_80));
+ break;
+ }
+
+ he->pkt_ext = 2;
+}
+
+static void
+mt7996_mcu_sta_he_6g_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
+{
+ struct sta_rec_he_6g_capa *he_6g;
+ struct tlv *tlv;
+
+ if (!sta->deflink.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;
+}
+
+static void
+mt7996_mcu_sta_ht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
+{
+ struct sta_rec_ht *ht;
+ struct tlv *tlv;
+
+ if (!sta->deflink.ht_cap.ht_supported)
+ return;
+
+ tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HT, sizeof(*ht));
+
+ ht = (struct sta_rec_ht *)tlv;
+ ht->ht_cap = cpu_to_le16(sta->deflink.ht_cap.cap);
+}
+
+static void
+mt7996_mcu_sta_vht_tlv(struct sk_buff *skb, struct ieee80211_sta *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)
+ 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;
+}
+
+static void
+mt7996_mcu_sta_amsdu_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ struct ieee80211_vif *vif, struct ieee80211_sta *sta)
+{
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ struct sta_rec_amsdu *amsdu;
+ struct tlv *tlv;
+
+ if (vif->type != NL80211_IFTYPE_STATION &&
+ vif->type != NL80211_IFTYPE_AP)
+ return;
+
+ if (!sta->deflink.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;
+
+ switch (sta->deflink.agg.max_amsdu_len) {
+ case IEEE80211_MAX_MPDU_LEN_VHT_11454:
+ amsdu->max_mpdu_size =
+ IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454;
+ return;
+ case IEEE80211_MAX_MPDU_LEN_HT_7935:
+ case IEEE80211_MAX_MPDU_LEN_VHT_7991:
+ amsdu->max_mpdu_size = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991;
+ return;
+ default:
+ amsdu->max_mpdu_size = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895;
+ return;
+ }
+}
+
+static inline bool
+mt7996_is_ebf_supported(struct mt7996_phy *phy, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, bool bfee)
+{
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ int tx_ant = hweight8(phy->mt76->antenna_mask) - 1;
+
+ if (vif->type != NL80211_IFTYPE_STATION &&
+ vif->type != NL80211_IFTYPE_AP)
+ return false;
+
+ if (!bfee && tx_ant < 2)
+ return false;
+
+ if (sta->deflink.he_cap.has_he) {
+ struct ieee80211_he_cap_elem *pe = &sta->deflink.he_cap.he_cap_elem;
+
+ if (bfee)
+ return mvif->cap.he_su_ebfee &&
+ HE_PHY(CAP3_SU_BEAMFORMER, pe->phy_cap_info[3]);
+ else
+ return mvif->cap.he_su_ebfer &&
+ 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 (bfee)
+ return mvif->cap.vht_su_ebfee &&
+ (cap & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE);
+ else
+ return mvif->cap.vht_su_ebfer &&
+ (cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE);
+ }
+
+ return false;
+}
+
+static void
+mt7996_mcu_sta_sounding_rate(struct sta_rec_bf *bf)
+{
+ bf->sounding_phy = MT_PHY_TYPE_OFDM;
+ bf->ndp_rate = 0; /* mcs0 */
+ bf->ndpa_rate = MT7996_CFEND_RATE_DEFAULT; /* ofdm 24m */
+ bf->rept_poll_rate = MT7996_CFEND_RATE_DEFAULT; /* ofdm 24m */
+}
+
+static void
+mt7996_mcu_sta_bfer_ht(struct ieee80211_sta *sta, struct mt7996_phy *phy,
+ struct sta_rec_bf *bf)
+{
+ struct ieee80211_mcs_info *mcs = &sta->deflink.ht_cap.mcs;
+ u8 n = 0;
+
+ bf->tx_mode = MT_PHY_TYPE_HT;
+
+ if ((mcs->tx_params & IEEE80211_HT_MCS_TX_RX_DIFF) &&
+ (mcs->tx_params & IEEE80211_HT_MCS_TX_DEFINED))
+ n = FIELD_GET(IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK,
+ mcs->tx_params);
+ else if (mcs->rx_mask[3])
+ n = 3;
+ else if (mcs->rx_mask[2])
+ n = 2;
+ else if (mcs->rx_mask[1])
+ n = 1;
+
+ bf->nrow = hweight8(phy->mt76->antenna_mask) - 1;
+ bf->ncol = min_t(u8, bf->nrow, n);
+ bf->ibf_ncol = n;
+}
+
+static void
+mt7996_mcu_sta_bfer_vht(struct ieee80211_sta *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 *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);
+ u8 tx_ant = hweight8(phy->mt76->antenna_mask) - 1;
+
+ bf->tx_mode = MT_PHY_TYPE_VHT;
+
+ if (explicit) {
+ u8 sts, snd_dim;
+
+ mt7996_mcu_sta_sounding_rate(bf);
+
+ sts = FIELD_GET(IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK,
+ pc->cap);
+ snd_dim = FIELD_GET(IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK,
+ vc->cap);
+ bf->nrow = min_t(u8, min_t(u8, snd_dim, sts), tx_ant);
+ bf->ncol = min_t(u8, nss_mcs, bf->nrow);
+ bf->ibf_ncol = bf->ncol;
+
+ if (sta->deflink.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 = nss_mcs;
+
+ if (sta->deflink.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)
+{
+ struct ieee80211_sta_he_cap *pc = &sta->deflink.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);
+ const struct ieee80211_he_cap_elem *ve = &vc->he_cap_elem;
+ u16 mcs_map = le16_to_cpu(pc->he_mcs_nss_supp.rx_mcs_80);
+ u8 nss_mcs = mt7996_mcu_get_sta_nss(mcs_map);
+ u8 snd_dim, sts;
+
+ bf->tx_mode = MT_PHY_TYPE_HE_SU;
+
+ mt7996_mcu_sta_sounding_rate(bf);
+
+ bf->trigger_su = HE_PHY(CAP6_TRIG_SU_BEAMFORMING_FB,
+ pe->phy_cap_info[6]);
+ bf->trigger_mu = HE_PHY(CAP6_TRIG_MU_BEAMFORMING_PARTIAL_BW_FB,
+ pe->phy_cap_info[6]);
+ snd_dim = HE_PHY(CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK,
+ ve->phy_cap_info[5]);
+ sts = HE_PHY(CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_MASK,
+ pe->phy_cap_info[4]);
+ bf->nrow = min_t(u8, snd_dim, sts);
+ bf->ncol = min_t(u8, nss_mcs, bf->nrow);
+ bf->ibf_ncol = bf->ncol;
+
+ if (sta->deflink.bandwidth != IEEE80211_STA_RX_BW_160)
+ return;
+
+ /* go over for 160MHz and 80p80 */
+ if (pe->phy_cap_info[0] &
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G) {
+ mcs_map = le16_to_cpu(pc->he_mcs_nss_supp.rx_mcs_160);
+ nss_mcs = mt7996_mcu_get_sta_nss(mcs_map);
+
+ bf->ncol_gt_bw80 = nss_mcs;
+ }
+
+ if (pe->phy_cap_info[0] &
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G) {
+ mcs_map = le16_to_cpu(pc->he_mcs_nss_supp.rx_mcs_80p80);
+ nss_mcs = mt7996_mcu_get_sta_nss(mcs_map);
+
+ if (bf->ncol_gt_bw80)
+ bf->ncol_gt_bw80 = min_t(u8, bf->ncol_gt_bw80, nss_mcs);
+ else
+ bf->ncol_gt_bw80 = nss_mcs;
+ }
+
+ snd_dim = HE_PHY(CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_MASK,
+ ve->phy_cap_info[5]);
+ sts = HE_PHY(CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_MASK,
+ pe->phy_cap_info[4]);
+
+ bf->nrow_gt_bw80 = min_t(int, snd_dim, sts);
+}
+
+static void
+mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ struct ieee80211_vif *vif, struct ieee80211_sta *sta)
+{
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_phy *phy = mvif->phy;
+ int tx_ant = hweight8(phy->mt76->antenna_mask) - 1;
+ struct sta_rec_bf *bf;
+ struct tlv *tlv;
+ const u8 matrix[4][4] = {
+ {0, 0, 0, 0},
+ {1, 1, 0, 0}, /* 2x1, 2x2, 2x3, 2x4 */
+ {2, 4, 4, 0}, /* 3x1, 3x2, 3x3, 3x4 */
+ {3, 5, 6, 0} /* 4x1, 4x2, 4x3, 4x4 */
+ };
+ bool ebf;
+
+ if (!(sta->deflink.ht_cap.ht_supported || sta->deflink.he_cap.has_he))
+ return;
+
+ ebf = mt7996_is_ebf_supported(phy, vif, sta, false);
+ if (!ebf && !dev->ibf)
+ return;
+
+ tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_BF, sizeof(*bf));
+ bf = (struct sta_rec_bf *)tlv;
+
+ /* he: eBF only, in accordance with spec
+ * vht: support eBF and iBF
+ * ht: iBF only, since mac80211 lacks of eBF support
+ */
+ if (sta->deflink.he_cap.has_he && ebf)
+ mt7996_mcu_sta_bfer_he(sta, vif, phy, bf);
+ 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);
+ else
+ return;
+
+ bf->bf_cap = ebf ? ebf : dev->ibf << 1;
+ bf->bw = sta->deflink.bandwidth;
+ bf->ibf_dbw = sta->deflink.bandwidth;
+ bf->ibf_nrow = tx_ant;
+
+ if (!ebf && sta->deflink.bandwidth <= IEEE80211_STA_RX_BW_40 && !bf->ncol)
+ bf->ibf_timeout = 0x48;
+ else
+ bf->ibf_timeout = 0x18;
+
+ if (ebf && bf->nrow != tx_ant)
+ bf->mem_20m = matrix[tx_ant][bf->ncol];
+ else
+ bf->mem_20m = matrix[bf->nrow][bf->ncol];
+
+ switch (sta->deflink.bandwidth) {
+ case IEEE80211_STA_RX_BW_160:
+ case IEEE80211_STA_RX_BW_80:
+ bf->mem_total = bf->mem_20m * 2;
+ break;
+ case IEEE80211_STA_RX_BW_40:
+ bf->mem_total = bf->mem_20m;
+ break;
+ case IEEE80211_STA_RX_BW_20:
+ default:
+ break;
+ }
+}
+
+static void
+mt7996_mcu_sta_bfee_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ struct ieee80211_vif *vif, struct ieee80211_sta *sta)
+{
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_phy *phy = mvif->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))
+ return;
+
+ if (!mt7996_is_ebf_supported(phy, vif, 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;
+
+ 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;
+
+ nrow = FIELD_GET(IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK,
+ pc->cap);
+ }
+
+ /* reply with identity matrix to avoid 2x2 BF negative gain */
+ bfee->fb_identity_matrix = (nrow == 1 && tx_ant == 2);
+}
+
+static void
+mt7996_mcu_sta_phy_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ struct ieee80211_vif *vif, struct ieee80211_sta *sta)
+{
+ struct sta_rec_phy *phy;
+ struct tlv *tlv;
+ u8 af = 0, mm = 0;
+
+ if (!sta->deflink.ht_cap.ht_supported && !sta->deflink.he_6ghz_capa.capa)
+ return;
+
+ tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_PHY, sizeof(*phy));
+
+ phy = (struct sta_rec_phy *)tlv;
+ if (sta->deflink.ht_cap.ht_supported) {
+ af = sta->deflink.ht_cap.ampdu_factor;
+ mm = sta->deflink.ht_cap.ampdu_density;
+ }
+
+ if (sta->deflink.vht_cap.vht_supported) {
+ u8 vht_af = FIELD_GET(IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK,
+ sta->deflink.vht_cap.cap);
+
+ af = max_t(u8, af, vht_af);
+ }
+
+ if (sta->deflink.he_6ghz_capa.capa) {
+ af = le16_get_bits(sta->deflink.he_6ghz_capa.capa,
+ IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP);
+ mm = le16_get_bits(sta->deflink.he_6ghz_capa.capa,
+ IEEE80211_HE_6GHZ_CAP_MIN_MPDU_START);
+ }
+
+ phy->ampdu = FIELD_PREP(IEEE80211_HT_AMPDU_PARM_FACTOR, af) |
+ FIELD_PREP(IEEE80211_HT_AMPDU_PARM_DENSITY, mm);
+ phy->max_ampdu_len = af;
+}
+
+static void
+mt7996_mcu_sta_hdrt_tlv(struct mt7996_dev *dev, struct sk_buff *skb)
+{
+ struct sta_rec_hdrt *hdrt;
+ struct tlv *tlv;
+
+ tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HDRT, sizeof(*hdrt));
+
+ hdrt = (struct sta_rec_hdrt *)tlv;
+ hdrt->hdrt_mode = 1;
+}
+
+static void
+mt7996_mcu_sta_hdr_trans_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct sta_rec_hdr_trans *hdr_trans;
+ struct mt76_wcid *wcid;
+ struct tlv *tlv;
+
+ tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HDR_TRANS, sizeof(*hdr_trans));
+ hdr_trans = (struct sta_rec_hdr_trans *)tlv;
+ hdr_trans->dis_rx_hdr_tran = true;
+
+ if (vif->type == NL80211_IFTYPE_STATION)
+ hdr_trans->to_ds = true;
+ else
+ hdr_trans->from_ds = true;
+
+ wcid = (struct mt76_wcid *)sta->drv_priv;
+ if (!wcid)
+ return;
+
+ hdr_trans->dis_rx_hdr_tran = !test_bit(MT_WCID_FLAG_HDR_TRANS, &wcid->flags);
+ if (test_bit(MT_WCID_FLAG_4ADDR, &wcid->flags)) {
+ hdr_trans->to_ds = true;
+ hdr_trans->from_ds = true;
+ }
+}
+
+static enum mcu_mmps_mode
+mt7996_mcu_get_mmps_mode(enum ieee80211_smps_mode smps)
+{
+ switch (smps) {
+ case IEEE80211_SMPS_OFF:
+ return MCU_MMPS_DISABLE;
+ case IEEE80211_SMPS_STATIC:
+ return MCU_MMPS_STATIC;
+ case IEEE80211_SMPS_DYNAMIC:
+ return MCU_MMPS_DYNAMIC;
+ default:
+ return MCU_MMPS_DISABLE;
+ }
+}
+
+int mt7996_mcu_set_fixed_rate_ctrl(struct mt7996_dev *dev,
+ void *data, u16 version)
+{
+ struct ra_fixed_rate *req;
+ struct uni_header hdr;
+ struct sk_buff *skb;
+ struct tlv *tlv;
+ int len;
+
+ len = sizeof(hdr) + sizeof(*req);
+
+ skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, len);
+ if (!skb)
+ return -ENOMEM;
+
+ skb_put_data(skb, &hdr, sizeof(hdr));
+
+ tlv = mt7996_mcu_add_uni_tlv(skb, UNI_RA_FIXED_RATE, sizeof(*req));
+ req = (struct ra_fixed_rate *)tlv;
+ req->version = cpu_to_le16(version);
+ memcpy(&req->rate, data, sizeof(req->rate));
+
+ return mt76_mcu_skb_send_msg(&dev->mt76, skb,
+ MCU_WM_UNI_CMD(RA), true);
+}
+
+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 mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt76_phy *mphy = mvif->phy->mt76;
+ struct cfg80211_chan_def *chandef = &mphy->chandef;
+ struct cfg80211_bitrate_mask *mask = &mvif->bitrate_mask;
+ enum nl80211_band band = chandef->chan->band;
+ struct sta_rec_ra *ra;
+ struct tlv *tlv;
+ u32 supp_rate = sta->deflink.supp_rates[band];
+ u32 cap = sta->wme ? STA_CAP_WMM : 0;
+
+ tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_RA, sizeof(*ra));
+ ra = (struct sta_rec_ra *)tlv;
+
+ ra->valid = true;
+ ra->auto_rate = true;
+ ra->phy_mode = mt76_connac_get_phy_mode(mphy, vif, band, sta);
+ ra->channel = chandef->chan->hw_value;
+ ra->bw = sta->deflink.bandwidth;
+ ra->phy.bw = sta->deflink.bandwidth;
+ ra->mmps_mode = mt7996_mcu_get_mmps_mode(sta->deflink.smps_mode);
+
+ if (supp_rate) {
+ supp_rate &= mask->control[band].legacy;
+ ra->rate_len = hweight32(supp_rate);
+
+ if (band == NL80211_BAND_2GHZ) {
+ ra->supp_mode = MODE_CCK;
+ ra->supp_cck_rate = supp_rate & GENMASK(3, 0);
+
+ if (ra->rate_len > 4) {
+ ra->supp_mode |= MODE_OFDM;
+ ra->supp_ofdm_rate = supp_rate >> 4;
+ }
+ } else {
+ ra->supp_mode = MODE_OFDM;
+ ra->supp_ofdm_rate = supp_rate;
+ }
+ }
+
+ if (sta->deflink.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);
+
+ cap |= STA_CAP_HT;
+ if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SGI_20)
+ cap |= STA_CAP_SGI_20;
+ if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SGI_40)
+ cap |= STA_CAP_SGI_40;
+ if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_TX_STBC)
+ cap |= STA_CAP_TX_STBC;
+ if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_RX_STBC)
+ cap |= STA_CAP_RX_STBC;
+ if (mvif->cap.ht_ldpc &&
+ (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_LDPC_CODING))
+ cap |= STA_CAP_LDPC;
+
+ mt7996_mcu_set_sta_ht_mcs(sta, ra->ht_mcs,
+ mask->control[band].ht_mcs);
+ ra->supp_ht_mcs = *(__le32 *)ra->ht_mcs;
+ }
+
+ if (sta->deflink.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);
+ ra->af = max_t(u8, ra->af, af);
+
+ cap |= STA_CAP_VHT;
+ if (sta->deflink.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)
+ cap |= STA_CAP_VHT_SGI_160;
+ if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_TXSTBC)
+ cap |= STA_CAP_VHT_TX_STBC;
+ if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_RXSTBC_1)
+ cap |= STA_CAP_VHT_RX_STBC;
+ if (mvif->cap.vht_ldpc &&
+ (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_RXLDPC))
+ cap |= STA_CAP_VHT_LDPC;
+
+ mt7996_mcu_set_sta_vht_mcs(sta, ra->supp_vht_mcs,
+ mask->control[band].vht_mcs);
+ }
+
+ if (sta->deflink.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,
+ IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP);
+ }
+ ra->sta_cap = cpu_to_le32(cap);
+}
+
+int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, 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;
+
+ skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76,
+ &msta->wcid,
+ MT7996_STA_UPDATE_MAX_SIZE);
+ if (IS_ERR(skb))
+ return PTR_ERR(skb);
+
+ /* firmware rc algorithm refers to sta_rec_he for HE control.
+ * once dev->rc_work changes the settings driver should also
+ * update sta_rec_he here.
+ */
+ if (changed)
+ mt7996_mcu_sta_he_tlv(skb, sta);
+
+ /* 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);
+
+ return mt76_mcu_skb_send_msg(&dev->mt76, skb,
+ MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
+}
+
+static int
+mt7996_mcu_add_group(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+#define MT_STA_BSS_GROUP 1
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_sta *msta;
+ struct {
+ u8 __rsv1[4];
+
+ __le16 tag;
+ __le16 len;
+ __le16 wlan_idx;
+ u8 __rsv2[2];
+ __le32 action;
+ __le32 val;
+ u8 __rsv3[8];
+ } __packed req = {
+ .tag = cpu_to_le16(UNI_VOW_DRR_CTRL),
+ .len = cpu_to_le16(sizeof(req) - 4),
+ .action = cpu_to_le32(MT_STA_BSS_GROUP),
+ .val = cpu_to_le32(mvif->mt76.idx % 16),
+ };
+
+ msta = sta ? (struct mt7996_sta *)sta->drv_priv : &mvif->sta;
+ req.wlan_idx = cpu_to_le16(msta->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 ieee80211_sta *sta, bool enable)
+{
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_sta *msta;
+ struct sk_buff *skb;
+ int ret;
+
+ msta = sta ? (struct mt7996_sta *)sta->drv_priv : &mvif->sta;
+
+ skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76,
+ &msta->wcid,
+ MT7996_STA_UPDATE_MAX_SIZE);
+ if (IS_ERR(skb))
+ return PTR_ERR(skb);
+
+ /* starec basic */
+ mt76_connac_mcu_sta_basic_tlv(skb, vif, sta, enable,
+ !rcu_access_pointer(dev->mt76.wcid[msta->wcid.idx]));
+ if (!enable)
+ goto out;
+
+ /* tag order is in accordance with firmware dependency. */
+ if (sta) {
+ /* starec phy */
+ mt7996_mcu_sta_phy_tlv(dev, skb, vif, sta);
+ /* starec hdrt mode */
+ mt7996_mcu_sta_hdrt_tlv(dev, skb);
+ /* starec bfer */
+ mt7996_mcu_sta_bfer_tlv(dev, skb, vif, sta);
+ /* starec ht */
+ mt7996_mcu_sta_ht_tlv(skb, sta);
+ /* starec vht */
+ mt7996_mcu_sta_vht_tlv(skb, sta);
+ /* starec uapsd */
+ mt76_connac_mcu_sta_uapsd(skb, vif, sta);
+ /* starec amsdu */
+ mt7996_mcu_sta_amsdu_tlv(dev, skb, vif, sta);
+ /* starec he */
+ mt7996_mcu_sta_he_tlv(skb, sta);
+ /* starec he 6g*/
+ mt7996_mcu_sta_he_6g_tlv(skb, sta);
+ /* TODO: starec muru */
+ /* starec bfee */
+ mt7996_mcu_sta_bfee_tlv(dev, skb, vif, sta);
+ /* starec hdr trans */
+ mt7996_mcu_sta_hdr_trans_tlv(dev, skb, vif, sta);
+ }
+
+ ret = mt7996_mcu_add_group(dev, vif, sta);
+ if (ret) {
+ dev_kfree_skb(skb);
+ return ret;
+ }
+out:
+ 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 mt76_connac_sta_key_conf *sta_key_conf,
+ struct sk_buff *skb,
+ struct ieee80211_key_conf *key,
+ enum set_key_cmd cmd)
+{
+ struct sta_rec_sec_uni *sec;
+ struct tlv *tlv;
+
+ tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_KEY_V2, sizeof(*sec));
+ sec = (struct sta_rec_sec_uni *)tlv;
+ sec->add = cmd;
+
+ if (cmd == SET_KEY) {
+ struct sec_key_uni *sec_key;
+ u8 cipher;
+
+ cipher = mt76_connac_mcu_get_cipher(key->cipher);
+ if (cipher == MCU_CIPHER_NONE)
+ return -EOPNOTSUPP;
+
+ sec_key = &sec->key[0];
+ sec_key->cipher_len = sizeof(*sec_key);
+
+ if (cipher == MCU_CIPHER_BIP_CMAC_128) {
+ sec_key->wlan_idx = cpu_to_le16(wcid->idx);
+ sec_key->cipher_id = MCU_CIPHER_AES_CCMP;
+ sec_key->key_id = sta_key_conf->keyidx;
+ sec_key->key_len = 16;
+ memcpy(sec_key->key, sta_key_conf->key, 16);
+
+ sec_key = &sec->key[1];
+ sec_key->wlan_idx = cpu_to_le16(wcid->idx);
+ sec_key->cipher_id = MCU_CIPHER_BIP_CMAC_128;
+ sec_key->cipher_len = sizeof(*sec_key);
+ sec_key->key_len = 16;
+ memcpy(sec_key->key, key->key, 16);
+ sec->n_cipher = 2;
+ } else {
+ sec_key->wlan_idx = cpu_to_le16(wcid->idx);
+ sec_key->cipher_id = cipher;
+ sec_key->key_id = key->keyidx;
+ sec_key->key_len = key->keylen;
+ memcpy(sec_key->key, key->key, key->keylen);
+
+ if (cipher == MCU_CIPHER_TKIP) {
+ /* Rx/Tx MIC keys are swapped */
+ memcpy(sec_key->key + 16, key->key + 24, 8);
+ memcpy(sec_key->key + 24, key->key + 16, 8);
+ }
+
+ /* store key_conf for BIP batch update */
+ if (cipher == MCU_CIPHER_AES_CCMP) {
+ memcpy(sta_key_conf->key, key->key, key->keylen);
+ sta_key_conf->keyidx = key->keyidx;
+ }
+
+ sec->n_cipher = 1;
+ }
+ } else {
+ sec->n_cipher = 0;
+ }
+
+ return 0;
+}
+
+int mt7996_mcu_add_key(struct mt76_dev *dev, struct ieee80211_vif *vif,
+ struct mt76_connac_sta_key_conf *sta_key_conf,
+ struct ieee80211_key_conf *key, int mcu_cmd,
+ struct mt76_wcid *wcid, enum set_key_cmd cmd)
+{
+ struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
+ struct sk_buff *skb;
+ int ret;
+
+ skb = __mt76_connac_mcu_alloc_sta_req(dev, mvif, wcid,
+ MT7996_STA_UPDATE_MAX_SIZE);
+ if (IS_ERR(skb))
+ return PTR_ERR(skb);
+
+ ret = mt7996_mcu_sta_key_tlv(wcid, sta_key_conf, skb, key, cmd);
+ if (ret)
+ return ret;
+
+ return mt76_mcu_skb_send_msg(dev, skb, mcu_cmd, true);
+}
+
+int mt7996_mcu_add_dev_info(struct mt7996_phy *phy,
+ struct ieee80211_vif *vif, bool enable)
+{
+ struct mt7996_dev *dev = phy->dev;
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct {
+ struct req_hdr {
+ u8 omac_idx;
+ u8 band_idx;
+ u8 __rsv[2];
+ } __packed hdr;
+ struct req_tlv {
+ __le16 tag;
+ __le16 len;
+ u8 active;
+ u8 __rsv;
+ u8 omac_addr[ETH_ALEN];
+ } __packed tlv;
+ } data = {
+ .hdr = {
+ .omac_idx = mvif->mt76.omac_idx,
+ .band_idx = mvif->mt76.band_idx,
+ },
+ .tlv = {
+ .tag = cpu_to_le16(DEV_INFO_ACTIVE),
+ .len = cpu_to_le16(sizeof(struct req_tlv)),
+ .active = enable,
+ },
+ };
+
+ if (mvif->mt76.omac_idx >= REPEATER_BSSID_START)
+ return mt7996_mcu_muar_config(phy, vif, false, enable);
+
+ memcpy(data.tlv.omac_addr, vif->addr, ETH_ALEN);
+ return mt76_mcu_send_msg(&dev->mt76, MCU_WMWA_UNI_CMD(DEV_INFO_UPDATE),
+ &data, sizeof(data), true);
+}
+
+static void
+mt7996_mcu_beacon_cntdwn(struct ieee80211_vif *vif, struct sk_buff *rskb,
+ struct sk_buff *skb,
+ struct ieee80211_mutable_offsets *offs)
+{
+ struct bss_bcn_cntdwn_tlv *info;
+ struct tlv *tlv;
+ u16 tag;
+
+ if (!offs->cntdwn_counter_offs[0])
+ return;
+
+ tag = vif->bss_conf.csa_active ? UNI_BSS_INFO_BCN_CSA : UNI_BSS_INFO_BCN_BCC;
+
+ tlv = mt7996_mcu_add_uni_tlv(rskb, tag, sizeof(*info));
+
+ info = (struct bss_bcn_cntdwn_tlv *)tlv;
+ info->cnt = skb->data[offs->cntdwn_counter_offs[0]];
+}
+
+static void
+mt7996_mcu_beacon_cont(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ struct sk_buff *rskb, struct sk_buff *skb,
+ struct bss_bcn_content_tlv *bcn,
+ struct ieee80211_mutable_offsets *offs)
+{
+ struct mt76_wcid *wcid = &dev->mt76.global_wcid;
+ u8 *buf;
+
+ bcn->pkt_len = cpu_to_le16(MT_TXD_SIZE + skb->len);
+ bcn->tim_ie_pos = cpu_to_le16(offs->tim_offset);
+
+ if (offs->cntdwn_counter_offs[0]) {
+ u16 offset = offs->cntdwn_counter_offs[0];
+
+ if (vif->bss_conf.csa_active)
+ bcn->csa_ie_pos = cpu_to_le16(offset - 4);
+ if (vif->bss_conf.color_change_active)
+ bcn->bcc_ie_pos = cpu_to_le16(offset - 3);
+ }
+
+ buf = (u8 *)bcn + sizeof(*bcn) - MAX_BEACON_SIZE;
+ mt7996_mac_write_txwi(dev, (__le32 *)buf, skb, wcid, 0, NULL,
+ BSS_CHANGED_BEACON);
+ memcpy(buf + MT_TXD_SIZE, skb->data, skb->len);
+}
+
+static void
+mt7996_mcu_beacon_check_caps(struct mt7996_phy *phy, struct ieee80211_vif *vif,
+ struct sk_buff *skb)
+{
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_vif_cap *vc = &mvif->cap;
+ const struct ieee80211_he_cap_elem *he;
+ const struct ieee80211_vht_cap *vht;
+ const struct ieee80211_ht_cap *ht;
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
+ const u8 *ie;
+ u32 len, bc;
+
+ /* Check missing configuration options to allow AP mode in mac80211
+ * to remain in sync with hostapd settings, and get a subset of
+ * beacon and hardware capabilities.
+ */
+ if (WARN_ON_ONCE(skb->len <= (mgmt->u.beacon.variable - skb->data)))
+ return;
+
+ memset(vc, 0, sizeof(*vc));
+
+ len = skb->len - (mgmt->u.beacon.variable - skb->data);
+
+ ie = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, mgmt->u.beacon.variable,
+ len);
+ if (ie && ie[1] >= sizeof(*ht)) {
+ ht = (void *)(ie + 2);
+ vc->ht_ldpc |= !!(le16_to_cpu(ht->cap_info) &
+ IEEE80211_HT_CAP_LDPC_CODING);
+ }
+
+ ie = cfg80211_find_ie(WLAN_EID_VHT_CAPABILITY, mgmt->u.beacon.variable,
+ len);
+ if (ie && ie[1] >= sizeof(*vht)) {
+ u32 pc = phy->mt76->sband_5g.sband.vht_cap.cap;
+
+ vht = (void *)(ie + 2);
+ bc = le32_to_cpu(vht->vht_cap_info);
+
+ vc->vht_ldpc |= !!(bc & IEEE80211_VHT_CAP_RXLDPC);
+ vc->vht_su_ebfer =
+ (bc & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE) &&
+ (pc & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE);
+ vc->vht_su_ebfee =
+ (bc & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE) &&
+ (pc & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE);
+ vc->vht_mu_ebfer =
+ (bc & IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE) &&
+ (pc & IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE);
+ vc->vht_mu_ebfee =
+ (bc & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE) &&
+ (pc & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE);
+ }
+
+ ie = cfg80211_find_ext_ie(WLAN_EID_EXT_HE_CAPABILITY,
+ mgmt->u.beacon.variable, len);
+ if (ie && ie[1] >= sizeof(*he) + 1) {
+ const struct ieee80211_sta_he_cap *pc =
+ mt76_connac_get_he_phy_cap(phy->mt76, vif);
+ const struct ieee80211_he_cap_elem *pe = &pc->he_cap_elem;
+
+ he = (void *)(ie + 3);
+
+ vc->he_ldpc =
+ HE_PHY(CAP1_LDPC_CODING_IN_PAYLOAD, pe->phy_cap_info[1]);
+ vc->he_su_ebfer =
+ HE_PHY(CAP3_SU_BEAMFORMER, he->phy_cap_info[3]) &&
+ HE_PHY(CAP3_SU_BEAMFORMER, pe->phy_cap_info[3]);
+ vc->he_su_ebfee =
+ HE_PHY(CAP4_SU_BEAMFORMEE, he->phy_cap_info[4]) &&
+ HE_PHY(CAP4_SU_BEAMFORMEE, pe->phy_cap_info[4]);
+ vc->he_mu_ebfer =
+ HE_PHY(CAP4_MU_BEAMFORMER, he->phy_cap_info[4]) &&
+ HE_PHY(CAP4_MU_BEAMFORMER, pe->phy_cap_info[4]);
+ }
+}
+
+int mt7996_mcu_add_beacon(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, int en)
+{
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct ieee80211_mutable_offsets offs;
+ struct ieee80211_tx_info *info;
+ struct sk_buff *skb, *rskb;
+ struct tlv *tlv;
+ struct bss_bcn_content_tlv *bcn;
+
+ rskb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mvif->mt76,
+ MT7996_BEACON_UPDATE_SIZE);
+ if (IS_ERR(rskb))
+ return PTR_ERR(rskb);
+
+ tlv = mt7996_mcu_add_uni_tlv(rskb,
+ UNI_BSS_INFO_BCN_CONTENT, sizeof(*bcn));
+ bcn = (struct bss_bcn_content_tlv *)tlv;
+ bcn->enable = en;
+
+ if (!en)
+ goto out;
+
+ skb = ieee80211_beacon_get_template(hw, vif, &offs, 0);
+ if (!skb)
+ return -EINVAL;
+
+ if (skb->len > MAX_BEACON_SIZE - MT_TXD_SIZE) {
+ dev_err(dev->mt76.dev, "Bcn size limit exceed\n");
+ dev_kfree_skb(skb);
+ return -EINVAL;
+ }
+
+ info = IEEE80211_SKB_CB(skb);
+ info->hw_queue |= FIELD_PREP(MT_TX_HW_QUEUE_PHY, phy->mt76->band_idx);
+
+ mt7996_mcu_beacon_check_caps(phy, vif, skb);
+
+ mt7996_mcu_beacon_cont(dev, vif, rskb, skb, bcn, &offs);
+ /* TODO: subtag - 11v MBSSID */
+ mt7996_mcu_beacon_cntdwn(vif, rskb, skb, &offs);
+ dev_kfree_skb(skb);
+out:
+ return mt76_mcu_skb_send_msg(&phy->dev->mt76, rskb,
+ MCU_WMWA_UNI_CMD(BSS_INFO_UPDATE), true);
+}
+
+int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev,
+ struct ieee80211_vif *vif, u32 changed)
+{
+#define OFFLOAD_TX_MODE_SU BIT(0)
+#define OFFLOAD_TX_MODE_MU BIT(1)
+ struct ieee80211_hw *hw = mt76_hw(dev);
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct cfg80211_chan_def *chandef = &mvif->phy->mt76->chandef;
+ enum nl80211_band band = chandef->chan->band;
+ struct mt76_wcid *wcid = &dev->mt76.global_wcid;
+ struct bss_inband_discovery_tlv *discov;
+ struct ieee80211_tx_info *info;
+ struct sk_buff *rskb, *skb = NULL;
+ struct tlv *tlv;
+ u8 *buf, interval;
+
+ rskb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mvif->mt76,
+ MT7996_INBAND_FRAME_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;
+ 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;
+ skb = ieee80211_get_unsol_bcast_probe_resp_tmpl(hw, vif);
+ }
+
+ if (!skb)
+ return -EINVAL;
+
+ if (skb->len > MAX_INBAND_FRAME_SIZE - MT_TXD_SIZE) {
+ dev_err(dev->mt76.dev, "inband discovery size limit exceed\n");
+ dev_kfree_skb(skb);
+ return -EINVAL;
+ }
+
+ info = IEEE80211_SKB_CB(skb);
+ info->control.vif = vif;
+ info->band = band;
+ info->hw_queue |= FIELD_PREP(MT_TX_HW_QUEUE_PHY, phy->mt76->band_idx);
+
+ tlv = mt7996_mcu_add_uni_tlv(rskb, UNI_BSS_INFO_OFFLOAD, sizeof(*discov));
+
+ discov = (struct bss_inband_discovery_tlv *)tlv;
+ discov->tx_mode = OFFLOAD_TX_MODE_SU;
+ /* 0: UNSOL PROBE RESP, 1: FILS DISCOV */
+ discov->tx_type = !!(changed & BSS_CHANGED_FILS_DISCOVERY);
+ discov->tx_interval = interval;
+ discov->prob_rsp_len = cpu_to_le16(MT_TXD_SIZE + skb->len);
+ discov->enable = true;
+ discov->wcid = cpu_to_le16(MT7996_WTBL_RESERVED);
+
+ buf = (u8 *)tlv + sizeof(*discov) - MAX_INBAND_FRAME_SIZE;
+
+ mt7996_mac_write_txwi(dev, (__le32 *)buf, skb, wcid, 0, NULL,
+ changed);
+
+ memcpy(buf + MT_TXD_SIZE, skb->data, skb->len);
+
+ dev_kfree_skb(skb);
+
+ return mt76_mcu_skb_send_msg(&dev->mt76, rskb,
+ MCU_WMWA_UNI_CMD(BSS_INFO_UPDATE), true);
+}
+
+static int mt7996_driver_own(struct mt7996_dev *dev, u8 band)
+{
+ mt76_wr(dev, MT_TOP_LPCR_HOST_BAND(band), MT_TOP_LPCR_HOST_DRV_OWN);
+ if (!mt76_poll_msec(dev, MT_TOP_LPCR_HOST_BAND(band),
+ MT_TOP_LPCR_HOST_FW_OWN_STAT, 0, 500)) {
+ dev_err(dev->mt76.dev, "Timeout for driver own\n");
+ return -EIO;
+ }
+
+ /* clear irq when the driver own success */
+ mt76_wr(dev, MT_TOP_LPCR_HOST_BAND_IRQ_STAT(band),
+ MT_TOP_LPCR_HOST_BAND_STAT);
+
+ return 0;
+}
+
+static u32 mt7996_patch_sec_mode(u32 key_info)
+{
+ u32 sec = u32_get_bits(key_info, MT7996_PATCH_SEC), key = 0;
+
+ if (key_info == GENMASK(31, 0) || sec == MT7996_SEC_MODE_PLAIN)
+ return 0;
+
+ if (sec == MT7996_SEC_MODE_AES)
+ key = u32_get_bits(key_info, MT7996_PATCH_AES_KEY);
+ else
+ key = u32_get_bits(key_info, MT7996_PATCH_SCRAMBLE_KEY);
+
+ return MT7996_SEC_ENCRYPT | MT7996_SEC_IV |
+ u32_encode_bits(key, MT7996_SEC_KEY_IDX);
+}
+
+static int mt7996_load_patch(struct mt7996_dev *dev)
+{
+ const struct mt7996_patch_hdr *hdr;
+ const struct firmware *fw = NULL;
+ int i, ret, sem;
+
+ sem = mt76_connac_mcu_patch_sem_ctrl(&dev->mt76, 1);
+ switch (sem) {
+ case PATCH_IS_DL:
+ return 0;
+ case PATCH_NOT_DL_SEM_SUCCESS:
+ break;
+ default:
+ dev_err(dev->mt76.dev, "Failed to get patch semaphore\n");
+ return -EAGAIN;
+ }
+
+ ret = request_firmware(&fw, MT7996_ROM_PATCH, dev->mt76.dev);
+ if (ret)
+ goto out;
+
+ if (!fw || !fw->data || fw->size < sizeof(*hdr)) {
+ dev_err(dev->mt76.dev, "Invalid firmware\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ hdr = (const struct mt7996_patch_hdr *)(fw->data);
+
+ dev_info(dev->mt76.dev, "HW/SW Version: 0x%x, Build Time: %.16s\n",
+ be32_to_cpu(hdr->hw_sw_ver), hdr->build_date);
+
+ for (i = 0; i < be32_to_cpu(hdr->desc.n_region); i++) {
+ struct mt7996_patch_sec *sec;
+ const u8 *dl;
+ u32 len, addr, sec_key_idx, mode = DL_MODE_NEED_RSP;
+
+ sec = (struct mt7996_patch_sec *)(fw->data + sizeof(*hdr) +
+ i * sizeof(*sec));
+ if ((be32_to_cpu(sec->type) & PATCH_SEC_TYPE_MASK) !=
+ PATCH_SEC_TYPE_INFO) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ addr = be32_to_cpu(sec->info.addr);
+ len = be32_to_cpu(sec->info.len);
+ sec_key_idx = be32_to_cpu(sec->info.sec_key_idx);
+ dl = fw->data + be32_to_cpu(sec->offs);
+
+ mode |= mt7996_patch_sec_mode(sec_key_idx);
+
+ ret = mt76_connac_mcu_init_download(&dev->mt76, addr, len,
+ mode);
+ if (ret) {
+ dev_err(dev->mt76.dev, "Download request failed\n");
+ goto out;
+ }
+
+ ret = __mt76_mcu_send_firmware(&dev->mt76, MCU_CMD(FW_SCATTER),
+ dl, len, 4096);
+ if (ret) {
+ dev_err(dev->mt76.dev, "Failed to send patch\n");
+ goto out;
+ }
+ }
+
+ ret = mt76_connac_mcu_start_patch(&dev->mt76);
+ if (ret)
+ dev_err(dev->mt76.dev, "Failed to start patch\n");
+
+out:
+ sem = mt76_connac_mcu_patch_sem_ctrl(&dev->mt76, 0);
+ switch (sem) {
+ case PATCH_REL_SEM_SUCCESS:
+ break;
+ default:
+ ret = -EAGAIN;
+ dev_err(dev->mt76.dev, "Failed to release patch semaphore\n");
+ break;
+ }
+ release_firmware(fw);
+
+ return ret;
+}
+
+static int
+mt7996_mcu_send_ram_firmware(struct mt7996_dev *dev,
+ const struct mt7996_fw_trailer *hdr,
+ const u8 *data, bool is_wa)
+{
+ int i, offset = 0;
+ u32 override = 0, option = 0;
+
+ for (i = 0; i < hdr->n_region; i++) {
+ const struct mt7996_fw_region *region;
+ int err;
+ u32 len, addr, mode;
+
+ region = (const struct mt7996_fw_region *)((const u8 *)hdr -
+ (hdr->n_region - i) * sizeof(*region));
+ mode = mt76_connac_mcu_gen_dl_mode(&dev->mt76,
+ region->feature_set, is_wa);
+ len = le32_to_cpu(region->len);
+ addr = le32_to_cpu(region->addr);
+
+ if (region->feature_set & FW_FEATURE_OVERRIDE_ADDR)
+ override = addr;
+
+ err = mt76_connac_mcu_init_download(&dev->mt76, addr, len,
+ mode);
+ if (err) {
+ dev_err(dev->mt76.dev, "Download request failed\n");
+ return err;
+ }
+
+ err = __mt76_mcu_send_firmware(&dev->mt76, MCU_CMD(FW_SCATTER),
+ data + offset, len, 4096);
+ if (err) {
+ dev_err(dev->mt76.dev, "Failed to send firmware.\n");
+ return err;
+ }
+
+ offset += len;
+ }
+
+ if (override)
+ option |= FW_START_OVERRIDE;
+
+ if (is_wa)
+ option |= FW_START_WORKING_PDA_CR4;
+
+ return mt76_connac_mcu_start_firmware(&dev->mt76, override, option);
+}
+
+static int mt7996_load_ram(struct mt7996_dev *dev)
+{
+ const struct mt7996_fw_trailer *hdr;
+ const struct firmware *fw;
+ int ret;
+
+ ret = request_firmware(&fw, MT7996_FIRMWARE_WM, dev->mt76.dev);
+ if (ret)
+ return ret;
+
+ if (!fw || !fw->data || fw->size < sizeof(*hdr)) {
+ dev_err(dev->mt76.dev, "Invalid firmware\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ hdr = (const struct mt7996_fw_trailer *)(fw->data + fw->size - sizeof(*hdr));
+
+ dev_info(dev->mt76.dev, "WM Firmware Version: %.10s, Build Time: %.15s\n",
+ hdr->fw_ver, hdr->build_date);
+
+ ret = mt7996_mcu_send_ram_firmware(dev, hdr, fw->data, false);
+ if (ret) {
+ dev_err(dev->mt76.dev, "Failed to start WM firmware\n");
+ goto out;
+ }
+
+ release_firmware(fw);
+
+ ret = request_firmware(&fw, MT7996_FIRMWARE_WA, dev->mt76.dev);
+ if (ret)
+ return ret;
+
+ if (!fw || !fw->data || fw->size < sizeof(*hdr)) {
+ dev_err(dev->mt76.dev, "Invalid firmware\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ hdr = (const struct mt7996_fw_trailer *)(fw->data + fw->size - sizeof(*hdr));
+
+ dev_info(dev->mt76.dev, "WA Firmware Version: %.10s, Build Time: %.15s\n",
+ hdr->fw_ver, hdr->build_date);
+
+ ret = mt7996_mcu_send_ram_firmware(dev, hdr, fw->data, true);
+ if (ret) {
+ dev_err(dev->mt76.dev, "Failed to start WA firmware\n");
+ goto out;
+ }
+
+ snprintf(dev->mt76.hw->wiphy->fw_version,
+ sizeof(dev->mt76.hw->wiphy->fw_version),
+ "%.10s-%.15s", hdr->fw_ver, hdr->build_date);
+
+out:
+ release_firmware(fw);
+
+ return ret;
+}
+
+static int
+mt7996_firmware_state(struct mt7996_dev *dev, bool wa)
+{
+ u32 state = FIELD_PREP(MT_TOP_MISC_FW_STATE,
+ wa ? FW_STATE_RDY : FW_STATE_FW_DOWNLOAD);
+
+ if (!mt76_poll_msec(dev, MT_TOP_MISC, MT_TOP_MISC_FW_STATE,
+ state, 1000)) {
+ dev_err(dev->mt76.dev, "Timeout for initializing firmware\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+static int mt7996_load_firmware(struct mt7996_dev *dev)
+{
+ int ret;
+
+ /* make sure fw is download state */
+ if (mt7996_firmware_state(dev, false)) {
+ /* restart firmware once */
+ __mt76_mcu_restart(&dev->mt76);
+ ret = mt7996_firmware_state(dev, false);
+ if (ret) {
+ dev_err(dev->mt76.dev,
+ "Firmware is not ready for download\n");
+ return ret;
+ }
+ }
+
+ ret = mt7996_load_patch(dev);
+ if (ret)
+ return ret;
+
+ ret = mt7996_load_ram(dev);
+ if (ret)
+ return ret;
+
+ ret = mt7996_firmware_state(dev, true);
+ if (ret)
+ return ret;
+
+ mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[MT_MCUQ_FWDL], false);
+
+ dev_dbg(dev->mt76.dev, "Firmware init done\n");
+
+ return 0;
+}
+
+int mt7996_mcu_fw_log_2_host(struct mt7996_dev *dev, u8 type, u8 ctrl)
+{
+ struct {
+ u8 _rsv[4];
+
+ __le16 tag;
+ __le16 len;
+ u8 ctrl;
+ u8 interval;
+ u8 _rsv2[2];
+ } __packed data = {
+ .tag = cpu_to_le16(UNI_WSYS_CONFIG_FW_LOG_CTRL),
+ .len = cpu_to_le16(sizeof(data) - 4),
+ .ctrl = ctrl,
+ };
+
+ if (type == MCU_FW_LOG_WA)
+ return mt76_mcu_send_msg(&dev->mt76, MCU_WA_UNI_CMD(WSYS_CONFIG),
+ &data, sizeof(data), true);
+
+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(WSYS_CONFIG), &data,
+ sizeof(data), true);
+}
+
+int mt7996_mcu_fw_dbg_ctrl(struct mt7996_dev *dev, u32 module, u8 level)
+{
+ struct {
+ u8 _rsv[4];
+
+ __le16 tag;
+ __le16 len;
+ __le32 module_idx;
+ u8 level;
+ u8 _rsv2[3];
+ } data = {
+ .tag = cpu_to_le16(UNI_WSYS_CONFIG_FW_DBG_CTRL),
+ .len = cpu_to_le16(sizeof(data) - 4),
+ .module_idx = cpu_to_le32(module),
+ .level = level,
+ };
+
+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(WSYS_CONFIG), &data,
+ sizeof(data), false);
+}
+
+static int mt7996_mcu_set_mwds(struct mt7996_dev *dev, bool enabled)
+{
+ struct {
+ u8 enable;
+ u8 _rsv[3];
+ } __packed req = {
+ .enable = enabled
+ };
+
+ return mt76_mcu_send_msg(&dev->mt76, MCU_WA_EXT_CMD(MWDS_SUPPORT), &req,
+ sizeof(req), false);
+}
+
+static void mt7996_add_rx_airtime_tlv(struct sk_buff *skb, u8 band_idx)
+{
+ struct vow_rx_airtime *req;
+ struct tlv *tlv;
+
+ tlv = mt7996_mcu_add_uni_tlv(skb, UNI_VOW_RX_AT_AIRTIME_CLR_EN, sizeof(*req));
+ req = (struct vow_rx_airtime *)tlv;
+ req->enable = true;
+ req->band = band_idx;
+
+ tlv = mt7996_mcu_add_uni_tlv(skb, UNI_VOW_RX_AT_AIRTIME_EN, sizeof(*req));
+ req = (struct vow_rx_airtime *)tlv;
+ req->enable = true;
+ req->band = band_idx;
+}
+
+static int
+mt7996_mcu_init_rx_airtime(struct mt7996_dev *dev)
+{
+ struct uni_header hdr = {};
+ struct sk_buff *skb;
+ int len, num;
+
+ num = 2 + 2 * (dev->dbdc_support + dev->tbtc_support);
+ len = sizeof(hdr) + num * sizeof(struct vow_rx_airtime);
+ skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, len);
+ if (!skb)
+ return -ENOMEM;
+
+ skb_put_data(skb, &hdr, sizeof(hdr));
+
+ mt7996_add_rx_airtime_tlv(skb, dev->mt76.phy.band_idx);
+
+ if (dev->dbdc_support)
+ mt7996_add_rx_airtime_tlv(skb, MT_BAND1);
+
+ if (dev->tbtc_support)
+ mt7996_add_rx_airtime_tlv(skb, MT_BAND2);
+
+ return mt76_mcu_skb_send_msg(&dev->mt76, skb,
+ MCU_WM_UNI_CMD(VOW), true);
+}
+
+static int
+mt7996_mcu_restart(struct mt76_dev *dev)
+{
+ struct {
+ u8 __rsv1[4];
+
+ __le16 tag;
+ __le16 len;
+ u8 power_mode;
+ u8 __rsv2[3];
+ } __packed req = {
+ .tag = cpu_to_le16(UNI_POWER_OFF),
+ .len = cpu_to_le16(sizeof(req) - 4),
+ .power_mode = 1,
+ };
+
+ return mt76_mcu_send_msg(dev, MCU_WM_UNI_CMD(POWER_CREL), &req,
+ sizeof(req), false);
+}
+
+int mt7996_mcu_init(struct mt7996_dev *dev)
+{
+ static const struct mt76_mcu_ops mt7996_mcu_ops = {
+ .headroom = sizeof(struct mt76_connac2_mcu_txd), /* reuse */
+ .mcu_skb_send_msg = mt7996_mcu_send_message,
+ .mcu_parse_response = mt7996_mcu_parse_response,
+ .mcu_restart = mt7996_mcu_restart,
+ };
+ int ret;
+
+ dev->mt76.mcu_ops = &mt7996_mcu_ops;
+
+ /* force firmware operation mode into normal state,
+ * which should be set before firmware download stage.
+ */
+ mt76_wr(dev, MT_SWDEF_MODE, MT_SWDEF_NORMAL_MODE);
+
+ ret = mt7996_driver_own(dev, 0);
+ if (ret)
+ return ret;
+ /* set driver own for band1 when two hif exist */
+ if (dev->hif2) {
+ ret = mt7996_driver_own(dev, 1);
+ if (ret)
+ return ret;
+ }
+
+ ret = mt7996_load_firmware(dev);
+ if (ret)
+ return ret;
+
+ set_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state);
+ ret = mt7996_mcu_fw_log_2_host(dev, MCU_FW_LOG_WM, 0);
+ if (ret)
+ return ret;
+
+ 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_init_rx_airtime(dev);
+ if (ret)
+ return ret;
+
+ return mt7996_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(SET),
+ MCU_WA_PARAM_RED, 0, 0);
+}
+
+void mt7996_mcu_exit(struct mt7996_dev *dev)
+{
+ __mt76_mcu_restart(&dev->mt76);
+ if (mt7996_firmware_state(dev, false)) {
+ dev_err(dev->mt76.dev, "Failed to exit mcu\n");
+ return;
+ }
+
+ mt76_wr(dev, MT_TOP_LPCR_HOST_BAND(0), MT_TOP_LPCR_HOST_FW_OWN);
+ if (dev->hif2)
+ mt76_wr(dev, MT_TOP_LPCR_HOST_BAND(1),
+ MT_TOP_LPCR_HOST_FW_OWN);
+ skb_queue_purge(&dev->mt76.mcu.res_q);
+}
+
+int mt7996_mcu_set_hdr_trans(struct mt7996_dev *dev, bool hdr_trans)
+{
+ struct {
+ u8 __rsv[4];
+ } __packed hdr;
+ struct hdr_trans_blacklist *req_blacklist;
+ struct hdr_trans_en *req_en;
+ struct sk_buff *skb;
+ struct tlv *tlv;
+ int len = MT7996_HDR_TRANS_MAX_SIZE + sizeof(hdr);
+
+ skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, len);
+ if (!skb)
+ return -ENOMEM;
+
+ skb_put_data(skb, &hdr, sizeof(hdr));
+
+ tlv = mt7996_mcu_add_uni_tlv(skb, UNI_HDR_TRANS_EN, sizeof(*req_en));
+ req_en = (struct hdr_trans_en *)tlv;
+ req_en->enable = hdr_trans;
+
+ tlv = mt7996_mcu_add_uni_tlv(skb, UNI_HDR_TRANS_VLAN,
+ sizeof(struct hdr_trans_vlan));
+
+ if (hdr_trans) {
+ tlv = mt7996_mcu_add_uni_tlv(skb, UNI_HDR_TRANS_BLACKLIST,
+ sizeof(*req_blacklist));
+ req_blacklist = (struct hdr_trans_blacklist *)tlv;
+ req_blacklist->enable = 1;
+ req_blacklist->type = cpu_to_le16(ETH_P_PAE);
+ }
+
+ return mt76_mcu_skb_send_msg(&dev->mt76, skb,
+ MCU_WM_UNI_CMD(RX_HDR_TRANS), true);
+}
+
+int mt7996_mcu_set_tx(struct mt7996_dev *dev, struct ieee80211_vif *vif)
+{
+#define MCU_EDCA_AC_PARAM 0
+#define WMM_AIFS_SET BIT(0)
+#define WMM_CW_MIN_SET BIT(1)
+#define WMM_CW_MAX_SET BIT(2)
+#define WMM_TXOP_SET BIT(3)
+#define WMM_PARAM_SET (WMM_AIFS_SET | WMM_CW_MIN_SET | \
+ WMM_CW_MAX_SET | WMM_TXOP_SET)
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct {
+ u8 bss_idx;
+ u8 __rsv[3];
+ } __packed hdr = {
+ .bss_idx = mvif->mt76.idx,
+ };
+ struct sk_buff *skb;
+ int len = sizeof(hdr) + IEEE80211_NUM_ACS * sizeof(struct edca);
+ int ac;
+
+ skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, len);
+ if (!skb)
+ return -ENOMEM;
+
+ skb_put_data(skb, &hdr, sizeof(hdr));
+
+ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+ struct ieee80211_tx_queue_params *q = &mvif->queue_params[ac];
+ struct edca *e;
+ struct tlv *tlv;
+
+ tlv = mt7996_mcu_add_uni_tlv(skb, MCU_EDCA_AC_PARAM, sizeof(*e));
+
+ e = (struct edca *)tlv;
+ e->set = WMM_PARAM_SET;
+ e->queue = ac + mvif->mt76.wmm_idx * MT7996_MAX_WMM_SETS;
+ e->aifs = q->aifs;
+ e->txop = cpu_to_le16(q->txop);
+
+ if (q->cw_min)
+ e->cw_min = fls(q->cw_min);
+ else
+ e->cw_min = 5;
+
+ if (q->cw_max)
+ e->cw_max = fls(q->cw_max);
+ else
+ e->cw_max = 10;
+ }
+
+ return mt76_mcu_skb_send_msg(&dev->mt76, skb,
+ MCU_WM_UNI_CMD(EDCA_UPDATE), true);
+}
+
+int mt7996_mcu_set_fcc5_lpn(struct mt7996_dev *dev, int val)
+{
+ struct {
+ u8 _rsv[4];
+
+ __le16 tag;
+ __le16 len;
+
+ __le32 ctrl;
+ __le16 min_lpn;
+ u8 rsv[2];
+ } __packed req = {
+ .tag = cpu_to_le16(UNI_RDD_CTRL_SET_TH),
+ .len = cpu_to_le16(sizeof(req) - 4),
+
+ .ctrl = cpu_to_le32(0x1),
+ .min_lpn = cpu_to_le16(val),
+ };
+
+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(RDD_CTRL),
+ &req, sizeof(req), true);
+}
+
+int mt7996_mcu_set_pulse_th(struct mt7996_dev *dev,
+ const struct mt7996_dfs_pulse *pulse)
+{
+ struct {
+ u8 _rsv[4];
+
+ __le16 tag;
+ __le16 len;
+
+ __le32 ctrl;
+
+ __le32 max_width; /* us */
+ __le32 max_pwr; /* dbm */
+ __le32 min_pwr; /* dbm */
+ __le32 min_stgr_pri; /* us */
+ __le32 max_stgr_pri; /* us */
+ __le32 min_cr_pri; /* us */
+ __le32 max_cr_pri; /* us */
+ } __packed req = {
+ .tag = cpu_to_le16(UNI_RDD_CTRL_SET_TH),
+ .len = cpu_to_le16(sizeof(req) - 4),
+
+ .ctrl = cpu_to_le32(0x3),
+
+#define __req_field(field) .field = cpu_to_le32(pulse->field)
+ __req_field(max_width),
+ __req_field(max_pwr),
+ __req_field(min_pwr),
+ __req_field(min_stgr_pri),
+ __req_field(max_stgr_pri),
+ __req_field(min_cr_pri),
+ __req_field(max_cr_pri),
+#undef __req_field
+ };
+
+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(RDD_CTRL),
+ &req, sizeof(req), true);
+}
+
+int mt7996_mcu_set_radar_th(struct mt7996_dev *dev, int index,
+ const struct mt7996_dfs_pattern *pattern)
+{
+ struct {
+ u8 _rsv[4];
+
+ __le16 tag;
+ __le16 len;
+
+ __le32 ctrl;
+ __le16 radar_type;
+
+ u8 enb;
+ u8 stgr;
+ u8 min_crpn;
+ u8 max_crpn;
+ u8 min_crpr;
+ u8 min_pw;
+ __le32 min_pri;
+ __le32 max_pri;
+ u8 max_pw;
+ u8 min_crbn;
+ u8 max_crbn;
+ u8 min_stgpn;
+ u8 max_stgpn;
+ u8 min_stgpr;
+ u8 rsv[2];
+ __le32 min_stgpr_diff;
+ } __packed req = {
+ .tag = cpu_to_le16(UNI_RDD_CTRL_SET_TH),
+ .len = cpu_to_le16(sizeof(req) - 4),
+
+ .ctrl = cpu_to_le32(0x2),
+ .radar_type = cpu_to_le16(index),
+
+#define __req_field_u8(field) .field = pattern->field
+#define __req_field_u32(field) .field = cpu_to_le32(pattern->field)
+ __req_field_u8(enb),
+ __req_field_u8(stgr),
+ __req_field_u8(min_crpn),
+ __req_field_u8(max_crpn),
+ __req_field_u8(min_crpr),
+ __req_field_u8(min_pw),
+ __req_field_u32(min_pri),
+ __req_field_u32(max_pri),
+ __req_field_u8(max_pw),
+ __req_field_u8(min_crbn),
+ __req_field_u8(max_crbn),
+ __req_field_u8(min_stgpn),
+ __req_field_u8(max_stgpn),
+ __req_field_u8(min_stgpr),
+ __req_field_u32(min_stgpr_diff),
+#undef __req_field_u8
+#undef __req_field_u32
+ };
+
+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(RDD_CTRL),
+ &req, sizeof(req), true);
+}
+
+static int
+mt7996_mcu_background_chain_ctrl(struct mt7996_phy *phy,
+ struct cfg80211_chan_def *chandef,
+ int cmd)
+{
+ struct mt7996_dev *dev = phy->dev;
+ struct mt76_phy *mphy = phy->mt76;
+ struct ieee80211_channel *chan = mphy->chandef.chan;
+ int freq = mphy->chandef.center_freq1;
+ struct mt7996_mcu_background_chain_ctrl req = {
+ .tag = cpu_to_le16(0),
+ .len = cpu_to_le16(sizeof(req) - 4),
+ .monitor_scan_type = 2, /* simple rx */
+ };
+
+ if (!chandef && cmd != CH_SWITCH_BACKGROUND_SCAN_STOP)
+ return -EINVAL;
+
+ if (!cfg80211_chandef_valid(&mphy->chandef))
+ return -EINVAL;
+
+ switch (cmd) {
+ case CH_SWITCH_BACKGROUND_SCAN_START: {
+ req.chan = chan->hw_value;
+ req.central_chan = ieee80211_frequency_to_channel(freq);
+ req.bw = mt76_connac_chan_bw(&mphy->chandef);
+ req.monitor_chan = chandef->chan->hw_value;
+ req.monitor_central_chan =
+ ieee80211_frequency_to_channel(chandef->center_freq1);
+ req.monitor_bw = mt76_connac_chan_bw(chandef);
+ req.band_idx = phy->mt76->band_idx;
+ req.scan_mode = 1;
+ break;
+ }
+ case CH_SWITCH_BACKGROUND_SCAN_RUNNING:
+ req.monitor_chan = chandef->chan->hw_value;
+ req.monitor_central_chan =
+ ieee80211_frequency_to_channel(chandef->center_freq1);
+ req.band_idx = phy->mt76->band_idx;
+ req.scan_mode = 2;
+ break;
+ case CH_SWITCH_BACKGROUND_SCAN_STOP:
+ req.chan = chan->hw_value;
+ req.central_chan = ieee80211_frequency_to_channel(freq);
+ req.bw = mt76_connac_chan_bw(&mphy->chandef);
+ req.tx_stream = hweight8(mphy->antenna_mask);
+ req.rx_stream = mphy->antenna_mask;
+ break;
+ default:
+ return -EINVAL;
+ }
+ req.band = chandef ? chandef->chan->band == NL80211_BAND_5GHZ : 1;
+
+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(OFFCH_SCAN_CTRL),
+ &req, sizeof(req), false);
+}
+
+int mt7996_mcu_rdd_background_enable(struct mt7996_phy *phy,
+ struct cfg80211_chan_def *chandef)
+{
+ struct mt7996_dev *dev = phy->dev;
+ int err, region;
+
+ if (!chandef) { /* disable offchain */
+ err = mt7996_mcu_rdd_cmd(dev, RDD_STOP, MT_RX_SEL2,
+ 0, 0);
+ if (err)
+ return err;
+
+ return mt7996_mcu_background_chain_ctrl(phy, NULL,
+ CH_SWITCH_BACKGROUND_SCAN_STOP);
+ }
+
+ err = mt7996_mcu_background_chain_ctrl(phy, chandef,
+ CH_SWITCH_BACKGROUND_SCAN_START);
+ if (err)
+ return err;
+
+ switch (dev->mt76.region) {
+ case NL80211_DFS_ETSI:
+ region = 0;
+ break;
+ case NL80211_DFS_JP:
+ region = 2;
+ break;
+ case NL80211_DFS_FCC:
+ default:
+ region = 1;
+ break;
+ }
+
+ return mt7996_mcu_rdd_cmd(dev, RDD_START, MT_RX_SEL2,
+ 0, region);
+}
+
+int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag)
+{
+ static const u8 ch_band[] = {
+ [NL80211_BAND_2GHZ] = 0,
+ [NL80211_BAND_5GHZ] = 1,
+ [NL80211_BAND_6GHZ] = 2,
+ };
+ struct mt7996_dev *dev = phy->dev;
+ struct cfg80211_chan_def *chandef = &phy->mt76->chandef;
+ int freq1 = chandef->center_freq1;
+ u8 band_idx = phy->mt76->band_idx;
+ struct {
+ /* fixed field */
+ u8 __rsv[4];
+
+ __le16 tag;
+ __le16 len;
+ u8 control_ch;
+ u8 center_ch;
+ u8 bw;
+ u8 tx_path_num;
+ u8 rx_path; /* mask or num */
+ u8 switch_reason;
+ u8 band_idx;
+ u8 center_ch2; /* for 80+80 only */
+ __le16 cac_case;
+ u8 channel_band;
+ u8 rsv0;
+ __le32 outband_freq;
+ u8 txpower_drop;
+ u8 ap_bw;
+ u8 ap_center_ch;
+ u8 rsv1[53];
+ } __packed req = {
+ .tag = cpu_to_le16(tag),
+ .len = cpu_to_le16(sizeof(req) - 4),
+ .control_ch = chandef->chan->hw_value,
+ .center_ch = ieee80211_frequency_to_channel(freq1),
+ .bw = mt76_connac_chan_bw(chandef),
+ .tx_path_num = hweight16(phy->mt76->chainmask),
+ .rx_path = phy->mt76->chainmask >> dev->chainshift[band_idx],
+ .band_idx = band_idx,
+ .channel_band = ch_band[chandef->chan->band],
+ };
+
+ if (tag == UNI_CHANNEL_RX_PATH ||
+ dev->mt76.hw->conf.flags & IEEE80211_CONF_MONITOR)
+ req.switch_reason = CH_SWITCH_NORMAL;
+ else if (phy->mt76->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)
+ req.switch_reason = CH_SWITCH_SCAN_BYPASS_DPD;
+ else if (!cfg80211_reg_can_beacon(phy->mt76->hw->wiphy, chandef,
+ NL80211_IFTYPE_AP))
+ req.switch_reason = CH_SWITCH_DFS;
+ else
+ req.switch_reason = CH_SWITCH_NORMAL;
+
+ if (tag == UNI_CHANNEL_SWITCH)
+ req.rx_path = hweight8(req.rx_path);
+
+ if (chandef->width == NL80211_CHAN_WIDTH_80P80) {
+ int freq2 = chandef->center_freq2;
+
+ req.center_ch2 = ieee80211_frequency_to_channel(freq2);
+ }
+
+ return mt76_mcu_send_msg(&dev->mt76, MCU_WMWA_UNI_CMD(CHANNEL_SWITCH),
+ &req, sizeof(req), true);
+}
+
+static int mt7996_mcu_set_eeprom_flash(struct mt7996_dev *dev)
+{
+#define MAX_PAGE_IDX_MASK GENMASK(7, 5)
+#define PAGE_IDX_MASK GENMASK(4, 2)
+#define PER_PAGE_SIZE 0x400
+ struct mt7996_mcu_eeprom req = {
+ .tag = cpu_to_le16(UNI_EFUSE_BUFFER_MODE),
+ .buffer_mode = EE_MODE_BUFFER
+ };
+ u16 eeprom_size = MT7996_EEPROM_SIZE;
+ u8 total = DIV_ROUND_UP(eeprom_size, PER_PAGE_SIZE);
+ u8 *eep = (u8 *)dev->mt76.eeprom.data;
+ int eep_len, i;
+
+ for (i = 0; i < total; i++, eep += eep_len) {
+ struct sk_buff *skb;
+ int ret, msg_len;
+
+ if (i == total - 1 && !!(eeprom_size % PER_PAGE_SIZE))
+ eep_len = eeprom_size % PER_PAGE_SIZE;
+ else
+ eep_len = PER_PAGE_SIZE;
+
+ msg_len = sizeof(req) + eep_len;
+ skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, msg_len);
+ if (!skb)
+ return -ENOMEM;
+
+ req.len = cpu_to_le16(msg_len - 4);
+ req.format = FIELD_PREP(MAX_PAGE_IDX_MASK, total - 1) |
+ FIELD_PREP(PAGE_IDX_MASK, i) | EE_FORMAT_WHOLE;
+ req.buf_len = cpu_to_le16(eep_len);
+
+ skb_put_data(skb, &req, sizeof(req));
+ skb_put_data(skb, eep, eep_len);
+
+ ret = mt76_mcu_skb_send_msg(&dev->mt76, skb,
+ MCU_WM_UNI_CMD(EFUSE_CTRL), true);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+int mt7996_mcu_set_eeprom(struct mt7996_dev *dev)
+{
+ struct mt7996_mcu_eeprom req = {
+ .tag = cpu_to_le16(UNI_EFUSE_BUFFER_MODE),
+ .len = cpu_to_le16(sizeof(req) - 4),
+ .buffer_mode = EE_MODE_EFUSE,
+ .format = EE_FORMAT_WHOLE
+ };
+
+ if (dev->flash_mode)
+ return mt7996_mcu_set_eeprom_flash(dev);
+
+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(EFUSE_CTRL),
+ &req, sizeof(req), true);
+}
+
+int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset)
+{
+ struct {
+ u8 _rsv[4];
+
+ __le16 tag;
+ __le16 len;
+ __le32 addr;
+ __le32 valid;
+ u8 data[16];
+ } __packed req = {
+ .tag = cpu_to_le16(UNI_EFUSE_ACCESS),
+ .len = cpu_to_le16(sizeof(req) - 4),
+ .addr = cpu_to_le32(round_down(offset,
+ MT7996_EEPROM_BLOCK_SIZE)),
+ };
+ struct sk_buff *skb;
+ bool valid;
+ 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;
+
+ valid = le32_to_cpu(*(__le32 *)(skb->data + 16));
+ if (valid) {
+ u32 addr = le32_to_cpu(*(__le32 *)(skb->data + 12));
+ u8 *buf = (u8 *)dev->mt76.eeprom.data + addr;
+
+ skb_pull(skb, 64);
+ memcpy(buf, skb->data, MT7996_EEPROM_BLOCK_SIZE);
+ }
+
+ dev_kfree_skb(skb);
+
+ return 0;
+}
+
+int mt7996_mcu_get_eeprom_free_block(struct mt7996_dev *dev, u8 *block_num)
+{
+ struct {
+ u8 _rsv[4];
+
+ __le16 tag;
+ __le16 len;
+ u8 num;
+ u8 version;
+ u8 die_idx;
+ u8 _rsv2;
+ } __packed req = {
+ .tag = cpu_to_le16(UNI_EFUSE_FREE_BLOCK),
+ .len = cpu_to_le16(sizeof(req) - 4),
+ .version = 2,
+ };
+ 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;
+
+ *block_num = *(u8 *)(skb->data + 8);
+ dev_kfree_skb(skb);
+
+ return 0;
+}
+
+int mt7996_mcu_get_chan_mib_info(struct mt7996_phy *phy, bool chan_switch)
+{
+ struct {
+ struct {
+ u8 band;
+ u8 __rsv[3];
+ } hdr;
+ struct {
+ __le16 tag;
+ __le16 len;
+ __le32 offs;
+ } data[4];
+ } __packed req = {
+ .hdr.band = phy->mt76->band_idx,
+ };
+ /* strict order */
+ static const u32 offs[] = {
+ UNI_MIB_TX_TIME,
+ UNI_MIB_RX_TIME,
+ UNI_MIB_OBSS_AIRTIME,
+ UNI_MIB_NON_WIFI_TIME,
+ };
+ struct mt76_channel_state *state = phy->mt76->chan_state;
+ struct mt76_channel_state *state_ts = &phy->state_ts;
+ struct mt7996_dev *dev = phy->dev;
+ struct mt7996_mcu_mib *res;
+ struct sk_buff *skb;
+ int i, ret;
+
+ for (i = 0; i < 4; i++) {
+ req.data[i].tag = cpu_to_le16(UNI_CMD_MIB_DATA);
+ req.data[i].len = cpu_to_le16(sizeof(req.data[i]));
+ req.data[i].offs = cpu_to_le32(offs[i]);
+ }
+
+ ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_WM_UNI_CMD_QUERY(GET_MIB_INFO),
+ &req, sizeof(req), true, &skb);
+ if (ret)
+ return ret;
+
+ skb_pull(skb, sizeof(req.hdr));
+
+ res = (struct mt7996_mcu_mib *)(skb->data);
+
+ if (chan_switch)
+ goto out;
+
+#define __res_u64(s) le64_to_cpu(res[s].data)
+ state->cc_tx += __res_u64(1) - state_ts->cc_tx;
+ state->cc_bss_rx += __res_u64(2) - state_ts->cc_bss_rx;
+ state->cc_rx += __res_u64(2) + __res_u64(3) - state_ts->cc_rx;
+ state->cc_busy += __res_u64(0) + __res_u64(1) + __res_u64(2) + __res_u64(3) -
+ state_ts->cc_busy;
+
+out:
+ state_ts->cc_tx = __res_u64(1);
+ state_ts->cc_bss_rx = __res_u64(2);
+ state_ts->cc_rx = __res_u64(2) + __res_u64(3);
+ state_ts->cc_busy = __res_u64(0) + __res_u64(1) + __res_u64(2) + __res_u64(3);
+#undef __res_u64
+
+ dev_kfree_skb(skb);
+
+ return 0;
+}
+
+int mt7996_mcu_set_ser(struct mt7996_dev *dev, u8 action, u8 val, u8 band)
+{
+ struct {
+ u8 rsv[4];
+
+ __le16 tag;
+ __le16 len;
+
+ union {
+ struct {
+ __le32 mask;
+ } __packed set;
+
+ struct {
+ u8 method;
+ u8 band;
+ u8 rsv2[2];
+ } __packed trigger;
+ };
+ } __packed req = {
+ .tag = cpu_to_le16(action),
+ .len = cpu_to_le16(sizeof(req) - 4),
+ };
+
+ switch (action) {
+ case UNI_CMD_SER_SET:
+ req.set.mask = cpu_to_le32(val);
+ break;
+ case UNI_CMD_SER_TRIGGER:
+ req.trigger.method = val;
+ req.trigger.band = band;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(SER),
+ &req, sizeof(req), false);
+}
+
+int mt7996_mcu_set_txbf(struct mt7996_dev *dev, u8 action)
+{
+#define MT7996_BF_MAX_SIZE sizeof(union bf_tag_tlv)
+#define BF_PROCESSING 4
+ struct uni_header hdr;
+ struct sk_buff *skb;
+ struct tlv *tlv;
+ int len = sizeof(hdr) + MT7996_BF_MAX_SIZE;
+
+ memset(&hdr, 0, sizeof(hdr));
+
+ skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, len);
+ if (!skb)
+ return -ENOMEM;
+
+ skb_put_data(skb, &hdr, sizeof(hdr));
+
+ switch (action) {
+ case BF_SOUNDING_ON: {
+ struct bf_sounding_on *req_snd_on;
+
+ tlv = mt7996_mcu_add_uni_tlv(skb, action, sizeof(*req_snd_on));
+ req_snd_on = (struct bf_sounding_on *)tlv;
+ req_snd_on->snd_mode = BF_PROCESSING;
+ break;
+ }
+ case BF_HW_EN_UPDATE: {
+ struct bf_hw_en_status_update *req_hw_en;
+
+ tlv = mt7996_mcu_add_uni_tlv(skb, action, sizeof(*req_hw_en));
+ req_hw_en = (struct bf_hw_en_status_update *)tlv;
+ req_hw_en->ebf = true;
+ req_hw_en->ibf = dev->ibf;
+ break;
+ }
+ case BF_MOD_EN_CTRL: {
+ struct bf_mod_en_ctrl *req_mod_en;
+
+ tlv = mt7996_mcu_add_uni_tlv(skb, action, sizeof(*req_mod_en));
+ req_mod_en = (struct bf_mod_en_ctrl *)tlv;
+ req_mod_en->bf_num = 2;
+ req_mod_en->bf_bitmap = GENMASK(0, 0);
+ break;
+ }
+ default:
+ return -EINVAL;
+ }
+
+ return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_WM_UNI_CMD(BF), true);
+}
+
+static int
+mt7996_mcu_enable_obss_spr(struct mt7996_phy *phy, u16 action, u8 val)
+{
+ struct mt7996_dev *dev = phy->dev;
+ struct {
+ u8 band_idx;
+ u8 __rsv[3];
+
+ __le16 tag;
+ __le16 len;
+
+ __le32 val;
+ } __packed req = {
+ .band_idx = phy->mt76->band_idx,
+ .tag = cpu_to_le16(action),
+ .len = cpu_to_le16(sizeof(req) - 4),
+ .val = cpu_to_le32(val),
+ };
+
+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(SR),
+ &req, sizeof(req), true);
+}
+
+static int
+mt7996_mcu_set_obss_spr_pd(struct mt7996_phy *phy,
+ struct ieee80211_he_obss_pd *he_obss_pd)
+{
+ struct mt7996_dev *dev = phy->dev;
+ u8 max_th = 82, non_srg_max_th = 62;
+ struct {
+ u8 band_idx;
+ u8 __rsv[3];
+
+ __le16 tag;
+ __le16 len;
+
+ u8 pd_th_non_srg;
+ u8 pd_th_srg;
+ u8 period_offs;
+ u8 rcpi_src;
+ __le16 obss_pd_min;
+ __le16 obss_pd_min_srg;
+ u8 resp_txpwr_mode;
+ u8 txpwr_restrict_mode;
+ u8 txpwr_ref;
+ u8 __rsv2[3];
+ } __packed req = {
+ .band_idx = phy->mt76->band_idx,
+ .tag = cpu_to_le16(UNI_CMD_SR_SET_PARAM),
+ .len = cpu_to_le16(sizeof(req) - 4),
+ .obss_pd_min = cpu_to_le16(max_th),
+ .obss_pd_min_srg = cpu_to_le16(max_th),
+ .txpwr_restrict_mode = 2,
+ .txpwr_ref = 21
+ };
+ int ret;
+
+ /* disable firmware dynamical PD asjustment */
+ ret = mt7996_mcu_enable_obss_spr(phy, UNI_CMD_SR_ENABLE_DPD, false);
+ if (ret)
+ return ret;
+
+ if (he_obss_pd->sr_ctrl &
+ IEEE80211_HE_SPR_NON_SRG_OBSS_PD_SR_DISALLOWED)
+ req.pd_th_non_srg = max_th;
+ else if (he_obss_pd->sr_ctrl & IEEE80211_HE_SPR_NON_SRG_OFFSET_PRESENT)
+ req.pd_th_non_srg = max_th - he_obss_pd->non_srg_max_offset;
+ else
+ req.pd_th_non_srg = non_srg_max_th;
+
+ if (he_obss_pd->sr_ctrl & IEEE80211_HE_SPR_SRG_INFORMATION_PRESENT)
+ req.pd_th_srg = max_th - he_obss_pd->max_offset;
+
+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(SR),
+ &req, sizeof(req), true);
+}
+
+static int
+mt7996_mcu_set_obss_spr_siga(struct mt7996_phy *phy, struct ieee80211_vif *vif,
+ 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->mt76.omac_idx;
+ struct {
+ u8 band_idx;
+ u8 __rsv[3];
+
+ __le16 tag;
+ __le16 len;
+
+ u8 omac;
+ u8 __rsv2[3];
+ u8 flag[20];
+ } __packed req = {
+ .band_idx = phy->mt76->band_idx,
+ .tag = cpu_to_le16(UNI_CMD_SR_SET_SIGA),
+ .len = cpu_to_le16(sizeof(req) - 4),
+ .omac = omac > HW_BSSID_MAX ? omac - 12 : omac,
+ };
+ int ret;
+
+ if (he_obss_pd->sr_ctrl & IEEE80211_HE_SPR_HESIGA_SR_VAL15_ALLOWED)
+ req.flag[req.omac] = 0xf;
+ else
+ return 0;
+
+ /* switch to normal AP mode */
+ ret = mt7996_mcu_enable_obss_spr(phy, UNI_CMD_SR_ENABLE_MODE, 0);
+ if (ret)
+ return ret;
+
+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(SR),
+ &req, sizeof(req), true);
+}
+
+static int
+mt7996_mcu_set_obss_spr_bitmap(struct mt7996_phy *phy,
+ struct ieee80211_he_obss_pd *he_obss_pd)
+{
+ struct mt7996_dev *dev = phy->dev;
+ struct {
+ u8 band_idx;
+ u8 __rsv[3];
+
+ __le16 tag;
+ __le16 len;
+
+ __le32 color_l[2];
+ __le32 color_h[2];
+ __le32 bssid_l[2];
+ __le32 bssid_h[2];
+ } __packed req = {
+ .band_idx = phy->mt76->band_idx,
+ .tag = cpu_to_le16(UNI_CMD_SR_SET_SRG_BITMAP),
+ .len = cpu_to_le16(sizeof(req) - 4),
+ };
+ u32 bitmap;
+
+ memcpy(&bitmap, he_obss_pd->bss_color_bitmap, sizeof(bitmap));
+ req.color_l[req.band_idx] = cpu_to_le32(bitmap);
+
+ memcpy(&bitmap, he_obss_pd->bss_color_bitmap + 4, sizeof(bitmap));
+ req.color_h[req.band_idx] = cpu_to_le32(bitmap);
+
+ memcpy(&bitmap, he_obss_pd->partial_bssid_bitmap, sizeof(bitmap));
+ req.bssid_l[req.band_idx] = cpu_to_le32(bitmap);
+
+ memcpy(&bitmap, he_obss_pd->partial_bssid_bitmap + 4, sizeof(bitmap));
+ req.bssid_h[req.band_idx] = cpu_to_le32(bitmap);
+
+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(SR), &req,
+ sizeof(req), true);
+}
+
+int mt7996_mcu_add_obss_spr(struct mt7996_phy *phy, struct ieee80211_vif *vif,
+ struct ieee80211_he_obss_pd *he_obss_pd)
+{
+ int ret;
+
+ /* enable firmware scene detection algorithms */
+ ret = mt7996_mcu_enable_obss_spr(phy, UNI_CMD_SR_ENABLE_SD,
+ sr_scene_detect);
+ if (ret)
+ return ret;
+
+ /* firmware dynamically adjusts PD threshold so skip manual control */
+ if (sr_scene_detect && !he_obss_pd->enable)
+ return 0;
+
+ /* enable spatial reuse */
+ ret = mt7996_mcu_enable_obss_spr(phy, UNI_CMD_SR_ENABLE,
+ he_obss_pd->enable);
+ if (ret)
+ return ret;
+
+ if (sr_scene_detect || !he_obss_pd->enable)
+ return 0;
+
+ ret = mt7996_mcu_enable_obss_spr(phy, UNI_CMD_SR_ENABLE_TX, true);
+ if (ret)
+ return ret;
+
+ /* set SRG/non-SRG OBSS PD threshold */
+ ret = mt7996_mcu_set_obss_spr_pd(phy, he_obss_pd);
+ if (ret)
+ return ret;
+
+ /* Set SR prohibit */
+ ret = mt7996_mcu_set_obss_spr_siga(phy, vif, he_obss_pd);
+ if (ret)
+ return ret;
+
+ /* set SRG BSS color/BSSID bitmap */
+ return mt7996_mcu_set_obss_spr_bitmap(phy, he_obss_pd);
+}
+
+int mt7996_mcu_update_bss_color(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ struct cfg80211_he_bss_color *he_bss_color)
+{
+ int len = sizeof(struct bss_req_hdr) + sizeof(struct bss_color_tlv);
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct bss_color_tlv *bss_color;
+ struct sk_buff *skb;
+ struct tlv *tlv;
+
+ skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mvif->mt76, len);
+ if (IS_ERR(skb))
+ return PTR_ERR(skb);
+
+ tlv = mt76_connac_mcu_add_tlv(skb, UNI_BSS_INFO_BSS_COLOR,
+ sizeof(*bss_color));
+ bss_color = (struct bss_color_tlv *)tlv;
+ bss_color->enable = he_bss_color->enabled;
+ bss_color->color = he_bss_color->color;
+
+ return mt76_mcu_skb_send_msg(&dev->mt76, skb,
+ MCU_WMWA_UNI_CMD(BSS_INFO_UPDATE), true);
+}
+
+#define TWT_AGRT_TRIGGER BIT(0)
+#define TWT_AGRT_ANNOUNCE BIT(1)
+#define TWT_AGRT_PROTECT BIT(2)
+
+int mt7996_mcu_twt_agrt_update(struct mt7996_dev *dev,
+ struct mt7996_vif *mvif,
+ struct mt7996_twt_flow *flow,
+ int cmd)
+{
+ struct {
+ u8 _rsv[4];
+
+ __le16 tag;
+ __le16 len;
+ u8 tbl_idx;
+ u8 cmd;
+ u8 own_mac_idx;
+ u8 flowid; /* 0xff for group id */
+ __le16 peer_id; /* specify the peer_id (msb=0)
+ * or group_id (msb=1)
+ */
+ u8 duration; /* 256 us */
+ u8 bss_idx;
+ __le64 start_tsf;
+ __le16 mantissa;
+ u8 exponent;
+ u8 is_ap;
+ u8 agrt_params;
+ u8 __rsv2[135];
+ } __packed req = {
+ .tag = cpu_to_le16(UNI_CMD_TWT_ARGT_UPDATE),
+ .len = cpu_to_le16(sizeof(req) - 4),
+ .tbl_idx = flow->table_id,
+ .cmd = cmd,
+ .own_mac_idx = mvif->mt76.omac_idx,
+ .flowid = flow->id,
+ .peer_id = cpu_to_le16(flow->wcid),
+ .duration = flow->duration,
+ .bss_idx = mvif->mt76.idx,
+ .start_tsf = cpu_to_le64(flow->tsf),
+ .mantissa = flow->mantissa,
+ .exponent = flow->exp,
+ .is_ap = true,
+ };
+
+ if (flow->protection)
+ req.agrt_params |= TWT_AGRT_PROTECT;
+ if (!flow->flowtype)
+ req.agrt_params |= TWT_AGRT_ANNOUNCE;
+ if (flow->trigger)
+ req.agrt_params |= TWT_AGRT_TRIGGER;
+
+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(TWT),
+ &req, sizeof(req), true);
+}
+
+void mt7996_mcu_set_pm(void *priv, u8 *mac, struct ieee80211_vif *vif)
+{
+#define EXIT_PM_STATE 0
+#define ENTER_PM_STATE 1
+ struct ieee80211_hw *hw = priv;
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ struct mt7996_phy *phy = mt7996_hw_phy(hw);
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct bss_power_save *ps;
+ struct sk_buff *skb;
+ struct tlv *tlv;
+ bool running = test_bit(MT76_STATE_RUNNING, &phy->mt76->state);
+
+ skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mvif->mt76,
+ MT7996_BSS_UPDATE_MAX_SIZE);
+ if (IS_ERR(skb))
+ return;
+
+ tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_PS, sizeof(*ps));
+ ps = (struct bss_power_save *)tlv;
+ ps->profile = running ? EXIT_PM_STATE : ENTER_PM_STATE;
+
+ mt76_mcu_skb_send_msg(&dev->mt76, skb,
+ MCU_WMWA_UNI_CMD(BSS_INFO_UPDATE), true);
+}
+
+int mt7996_mcu_set_rts_thresh(struct mt7996_phy *phy, u32 val)
+{
+ struct {
+ u8 band_idx;
+ u8 _rsv[3];
+
+ __le16 tag;
+ __le16 len;
+ __le32 len_thresh;
+ __le32 pkt_thresh;
+ } __packed req = {
+ .band_idx = phy->mt76->band_idx,
+ .tag = cpu_to_le16(UNI_BAND_CONFIG_RTS_THRESHOLD),
+ .len = cpu_to_le16(sizeof(req) - 4),
+ .len_thresh = cpu_to_le32(val),
+ .pkt_thresh = cpu_to_le32(0x2),
+ };
+
+ return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(BAND_CONFIG),
+ &req, sizeof(req), true);
+}
+
+int mt7996_mcu_set_radio_en(struct mt7996_phy *phy, bool enable)
+{
+ struct {
+ u8 band_idx;
+ u8 _rsv[3];
+
+ __le16 tag;
+ __le16 len;
+ u8 enable;
+ u8 _rsv2[3];
+ } __packed req = {
+ .band_idx = phy->mt76->band_idx,
+ .tag = cpu_to_le16(UNI_BAND_CONFIG_RADIO_ENABLE),
+ .len = cpu_to_le16(sizeof(req) - 4),
+ .enable = enable,
+ };
+
+ return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(BAND_CONFIG),
+ &req, sizeof(req), true);
+}
+
+int mt7996_mcu_rdd_cmd(struct mt7996_dev *dev, int cmd, u8 index,
+ u8 rx_sel, u8 val)
+{
+ struct {
+ u8 _rsv[4];
+
+ __le16 tag;
+ __le16 len;
+
+ u8 ctrl;
+ u8 rdd_idx;
+ u8 rdd_rx_sel;
+ u8 val;
+ u8 rsv[4];
+ } __packed req = {
+ .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,
+ .val = val,
+ };
+
+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(RDD_CTRL),
+ &req, sizeof(req), true);
+}
+
+int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ 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->sta;
+
+ skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76,
+ &msta->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, sta);
+ return mt76_mcu_skb_send_msg(&dev->mt76, skb,
+ MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true);
+}
+
+int mt7996_mcu_rf_regval(struct mt7996_dev *dev, u32 regidx, u32 *val, bool set)
+{
+ struct {
+ u8 __rsv1[4];
+
+ __le16 tag;
+ __le16 len;
+ __le16 idx;
+ u8 __rsv2[2];
+ __le32 ofs;
+ __le32 data;
+ } __packed *res, req = {
+ .tag = cpu_to_le16(UNI_CMD_ACCESS_RF_REG_BASIC),
+ .len = cpu_to_le16(sizeof(req) - 4),
+
+ .idx = cpu_to_le16(u32_get_bits(regidx, GENMASK(31, 24))),
+ .ofs = cpu_to_le32(u32_get_bits(regidx, GENMASK(23, 0))),
+ .data = set ? cpu_to_le32(*val) : 0,
+ };
+ struct sk_buff *skb;
+ int ret;
+
+ if (set)
+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(REG_ACCESS),
+ &req, sizeof(req), true);
+
+ ret = mt76_mcu_send_and_get_msg(&dev->mt76,
+ MCU_WM_UNI_CMD_QUERY(REG_ACCESS),
+ &req, sizeof(req), true, &skb);
+ if (ret)
+ return ret;
+
+ res = (void *)skb->data;
+ *val = le32_to_cpu(res->data);
+ dev_kfree_skb(skb);
+
+ return 0;
+}
+
+int mt7996_mcu_set_rro(struct mt7996_dev *dev, u16 tag, u8 val)
+{
+ struct {
+ u8 __rsv1[4];
+
+ __le16 tag;
+ __le16 len;
+
+ union {
+ struct {
+ u8 type;
+ u8 __rsv2[3];
+ } __packed platform_type;
+ struct {
+ u8 type;
+ u8 dest;
+ u8 __rsv2[2];
+ } __packed bypass_mode;
+ struct {
+ u8 path;
+ u8 __rsv2[3];
+ } __packed txfree_path;
+ };
+ } __packed req = {
+ .tag = cpu_to_le16(tag),
+ .len = cpu_to_le16(sizeof(req) - 4),
+ };
+
+ switch (tag) {
+ case UNI_RRO_SET_PLATFORM_TYPE:
+ req.platform_type.type = val;
+ break;
+ case UNI_RRO_SET_BYPASS_MODE:
+ req.bypass_mode.type = val;
+ break;
+ case UNI_RRO_SET_TXFREE_PATH:
+ req.txfree_path.path = val;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(RRO), &req,
+ sizeof(req), true);
+}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h
new file mode 100644
index 000000000000..6084b2337598
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h
@@ -0,0 +1,669 @@
+/* SPDX-License-Identifier: ISC */
+/*
+ * Copyright (C) 2022 MediaTek Inc.
+ */
+
+#ifndef __MT7996_MCU_H
+#define __MT7996_MCU_H
+
+#include "../mt76_connac_mcu.h"
+
+struct mt7996_mcu_rxd {
+ __le32 rxd[8];
+
+ __le16 len;
+ __le16 pkt_type_id;
+
+ u8 eid;
+ u8 seq;
+ u8 option;
+ u8 __rsv;
+
+ u8 ext_eid;
+ u8 __rsv1[2];
+ u8 s2d_index;
+};
+
+struct mt7996_mcu_uni_event {
+ u8 cid;
+ u8 __rsv[3];
+ __le32 status; /* 0: success, others: fail */
+} __packed;
+
+struct mt7996_mcu_csa_notify {
+ struct mt7996_mcu_rxd rxd;
+
+ u8 omac_idx;
+ u8 csa_count;
+ u8 band_idx;
+ u8 rsv;
+} __packed;
+
+struct mt7996_mcu_rdd_report {
+ struct mt7996_mcu_rxd rxd;
+
+ u8 __rsv1[4];
+
+ __le16 tag;
+ __le16 len;
+
+ u8 band_idx;
+ u8 long_detected;
+ u8 constant_prf_detected;
+ u8 staggered_prf_detected;
+ u8 radar_type_idx;
+ u8 periodic_pulse_num;
+ u8 long_pulse_num;
+ u8 hw_pulse_num;
+
+ u8 out_lpn;
+ u8 out_spn;
+ u8 out_crpn;
+ u8 out_crpw;
+ u8 out_crbn;
+ u8 out_stgpn;
+ u8 out_stgpw;
+
+ u8 __rsv2;
+
+ __le32 out_pri_const;
+ __le32 out_pri_stg[3];
+ __le32 out_pri_stg_dmin;
+
+ struct {
+ __le32 start;
+ __le16 pulse_width;
+ __le16 pulse_power;
+ u8 mdrdy_flag;
+ u8 rsv[3];
+ } long_pulse[32];
+
+ struct {
+ __le32 start;
+ __le16 pulse_width;
+ __le16 pulse_power;
+ u8 mdrdy_flag;
+ u8 rsv[3];
+ } periodic_pulse[32];
+
+ struct {
+ __le32 start;
+ __le16 pulse_width;
+ __le16 pulse_power;
+ u8 sc_pass;
+ u8 sw_reset;
+ u8 mdrdy_flag;
+ u8 tx_active;
+ } hw_pulse[32];
+} __packed;
+
+struct mt7996_mcu_background_chain_ctrl {
+ u8 _rsv[4];
+
+ __le16 tag;
+ __le16 len;
+
+ u8 chan; /* primary channel */
+ u8 central_chan; /* central channel */
+ u8 bw;
+ u8 tx_stream;
+ u8 rx_stream;
+
+ u8 monitor_chan; /* monitor channel */
+ u8 monitor_central_chan;/* monitor central channel */
+ u8 monitor_bw;
+ u8 monitor_tx_stream;
+ u8 monitor_rx_stream;
+
+ u8 scan_mode; /* 0: ScanStop
+ * 1: ScanStart
+ * 2: ScanRunning
+ */
+ u8 band_idx; /* DBDC */
+ u8 monitor_scan_type;
+ u8 band; /* 0: 2.4GHz, 1: 5GHz */
+ u8 rsv[2];
+} __packed;
+
+struct mt7996_mcu_eeprom {
+ u8 _rsv[4];
+
+ __le16 tag;
+ __le16 len;
+ u8 buffer_mode;
+ u8 format;
+ __le16 buf_len;
+} __packed;
+
+struct mt7996_mcu_phy_rx_info {
+ u8 category;
+ u8 rate;
+ u8 mode;
+ u8 nsts;
+ u8 gi;
+ u8 coding;
+ u8 stbc;
+ u8 bw;
+};
+
+struct mt7996_mcu_mib {
+ __le16 tag;
+ __le16 len;
+ __le32 offs;
+ __le64 data;
+} __packed;
+
+enum mt7996_chan_mib_offs {
+ UNI_MIB_OBSS_AIRTIME = 26,
+ UNI_MIB_NON_WIFI_TIME = 27,
+ UNI_MIB_TX_TIME = 28,
+ UNI_MIB_RX_TIME = 29
+};
+
+struct edca {
+ __le16 tag;
+ __le16 len;
+
+ u8 queue;
+ u8 set;
+ u8 cw_min;
+ u8 cw_max;
+ __le16 txop;
+ u8 aifs;
+ u8 __rsv;
+};
+
+#define MCU_PQ_ID(p, q) (((p) << 15) | ((q) << 10))
+#define MCU_PKT_ID 0xa0
+
+enum {
+ MCU_FW_LOG_WM,
+ MCU_FW_LOG_WA,
+ MCU_FW_LOG_TO_HOST,
+ MCU_FW_LOG_RELAY = 16
+};
+
+enum {
+ MCU_TWT_AGRT_ADD,
+ MCU_TWT_AGRT_MODIFY,
+ MCU_TWT_AGRT_DELETE,
+ MCU_TWT_AGRT_TEARDOWN,
+ MCU_TWT_AGRT_GET_TSF,
+};
+
+enum {
+ MCU_WA_PARAM_CMD_QUERY,
+ MCU_WA_PARAM_CMD_SET,
+ MCU_WA_PARAM_CMD_CAPABILITY,
+ MCU_WA_PARAM_CMD_DEBUG,
+};
+
+enum {
+ MCU_WA_PARAM_PDMA_RX = 0x04,
+ MCU_WA_PARAM_CPU_UTIL = 0x0b,
+ MCU_WA_PARAM_RED = 0x0e,
+ MCU_WA_PARAM_HW_PATH_HIF_VER = 0x2f,
+};
+
+enum mcu_mmps_mode {
+ MCU_MMPS_STATIC,
+ MCU_MMPS_DYNAMIC,
+ MCU_MMPS_RSV,
+ MCU_MMPS_DISABLE,
+};
+
+struct bss_rate_tlv {
+ __le16 tag;
+ __le16 len;
+ u8 __rsv1[4];
+ __le16 bc_trans;
+ __le16 mc_trans;
+ u8 short_preamble;
+ u8 bc_fixed_rate;
+ u8 mc_fixed_rate;
+ u8 __rsv2[1];
+} __packed;
+
+struct bss_ra_tlv {
+ __le16 tag;
+ __le16 len;
+ u8 short_preamble;
+ u8 force_sgi;
+ u8 force_gf;
+ u8 ht_mode;
+ u8 se_off;
+ u8 antenna_idx;
+ __le16 max_phyrate;
+ u8 force_tx_streams;
+ u8 __rsv[3];
+} __packed;
+
+struct bss_rlm_tlv {
+ __le16 tag;
+ __le16 len;
+ u8 control_channel;
+ u8 center_chan;
+ u8 center_chan2;
+ u8 bw;
+ u8 tx_streams;
+ u8 rx_streams;
+ u8 ht_op_info;
+ u8 sco;
+ u8 band;
+ u8 __rsv[3];
+} __packed;
+
+struct bss_color_tlv {
+ __le16 tag;
+ __le16 len;
+ u8 enable;
+ u8 color;
+ u8 rsv[2];
+} __packed;
+
+struct bss_inband_discovery_tlv {
+ __le16 tag;
+ __le16 len;
+ u8 tx_type;
+ u8 tx_mode;
+ u8 tx_interval;
+ u8 enable;
+ __le16 wcid;
+ __le16 prob_rsp_len;
+#define MAX_INBAND_FRAME_SIZE 512
+ u8 pkt[MAX_INBAND_FRAME_SIZE];
+} __packed;
+
+struct bss_bcn_content_tlv {
+ __le16 tag;
+ __le16 len;
+ __le16 tim_ie_pos;
+ __le16 csa_ie_pos;
+ __le16 bcc_ie_pos;
+ u8 enable;
+ u8 type;
+ __le16 pkt_len;
+#define MAX_BEACON_SIZE 512
+ u8 pkt[MAX_BEACON_SIZE];
+} __packed;
+
+struct bss_bcn_cntdwn_tlv {
+ __le16 tag;
+ __le16 len;
+ u8 cnt;
+ u8 rsv[3];
+} __packed;
+
+struct bss_bcn_mbss_tlv {
+ __le16 tag;
+ __le16 len;
+ __le32 bitmap;
+#define MAX_BEACON_NUM 32
+ __le16 offset[MAX_BEACON_NUM];
+} __packed __aligned(4);
+
+struct bss_txcmd_tlv {
+ __le16 tag;
+ __le16 len;
+ u8 txcmd_mode;
+ u8 __rsv[3];
+} __packed;
+
+struct bss_sec_tlv {
+ __le16 tag;
+ __le16 len;
+ u8 __rsv1[2];
+ u8 cipher;
+ u8 __rsv2[1];
+} __packed;
+
+struct bss_power_save {
+ __le16 tag;
+ __le16 len;
+ u8 profile;
+ u8 _rsv[3];
+} __packed;
+
+struct bss_mld_tlv {
+ __le16 tag;
+ __le16 len;
+ u8 group_mld_id;
+ u8 own_mld_id;
+ u8 mac_addr[ETH_ALEN];
+ u8 remap_idx;
+ u8 __rsv[3];
+} __packed;
+
+struct sta_rec_ba_uni {
+ __le16 tag;
+ __le16 len;
+ u8 tid;
+ u8 ba_type;
+ u8 amsdu;
+ u8 ba_en;
+ __le16 ssn;
+ __le16 winsize;
+ u8 ba_rdd_rro;
+ u8 __rsv[3];
+} __packed;
+
+struct sec_key_uni {
+ __le16 wlan_idx;
+ u8 mgmt_prot;
+ u8 cipher_id;
+ u8 cipher_len;
+ u8 key_id;
+ u8 key_len;
+ u8 need_resp;
+ u8 key[32];
+} __packed;
+
+struct sta_rec_sec_uni {
+ __le16 tag;
+ __le16 len;
+ u8 add;
+ u8 n_cipher;
+ u8 rsv[2];
+
+ struct sec_key_uni key[2];
+} __packed;
+
+struct sta_rec_hdrt {
+ __le16 tag;
+ __le16 len;
+ u8 hdrt_mode;
+ u8 rsv[3];
+} __packed;
+
+struct sta_rec_hdr_trans {
+ __le16 tag;
+ __le16 len;
+ u8 from_ds;
+ u8 to_ds;
+ u8 dis_rx_hdr_tran;
+ u8 rsv;
+} __packed;
+
+struct hdr_trans_en {
+ __le16 tag;
+ __le16 len;
+ u8 enable;
+ u8 check_bssid;
+ u8 mode;
+ u8 __rsv;
+} __packed;
+
+struct hdr_trans_vlan {
+ __le16 tag;
+ __le16 len;
+ u8 insert_vlan;
+ u8 remove_vlan;
+ u8 tid;
+ u8 __rsv;
+} __packed;
+
+struct hdr_trans_blacklist {
+ __le16 tag;
+ __le16 len;
+ u8 idx;
+ u8 enable;
+ __le16 type;
+} __packed;
+
+struct uni_header {
+ u8 __rsv[4];
+} __packed;
+
+struct vow_rx_airtime {
+ __le16 tag;
+ __le16 len;
+
+ u8 enable;
+ u8 band;
+ u8 __rsv[2];
+} __packed;
+
+struct bf_sounding_on {
+ __le16 tag;
+ __le16 len;
+
+ u8 snd_mode;
+ u8 sta_num;
+ u8 __rsv[2];
+ __le16 wlan_id[4];
+ __le32 snd_period;
+} __packed;
+
+struct bf_hw_en_status_update {
+ __le16 tag;
+ __le16 len;
+
+ bool ebf;
+ bool ibf;
+ u8 __rsv[2];
+} __packed;
+
+struct bf_mod_en_ctrl {
+ __le16 tag;
+ __le16 len;
+
+ u8 bf_num;
+ u8 bf_bitmap;
+ u8 bf_sel[8];
+ u8 __rsv[2];
+} __packed;
+
+union bf_tag_tlv {
+ struct bf_sounding_on bf_snd;
+ struct bf_hw_en_status_update bf_hw_en;
+ struct bf_mod_en_ctrl bf_mod_en;
+};
+
+struct ra_rate {
+ __le16 wlan_idx;
+ u8 mode;
+ u8 stbc;
+ __le16 gi;
+ u8 bw;
+ u8 ldpc;
+ u8 mcs;
+ u8 nss;
+ __le16 ltf;
+ u8 spe;
+ u8 preamble;
+ u8 __rsv[2];
+} __packed;
+
+struct ra_fixed_rate {
+ __le16 tag;
+ __le16 len;
+
+ __le16 version;
+ struct ra_rate rate;
+} __packed;
+
+enum {
+ UNI_RA_FIXED_RATE = 0xf,
+};
+
+#define MT7996_HDR_TRANS_MAX_SIZE (sizeof(struct hdr_trans_en) + \
+ sizeof(struct hdr_trans_vlan) + \
+ sizeof(struct hdr_trans_blacklist))
+
+enum {
+ UNI_HDR_TRANS_EN,
+ UNI_HDR_TRANS_VLAN,
+ UNI_HDR_TRANS_BLACKLIST,
+};
+
+enum {
+ RATE_PARAM_FIXED = 3,
+ RATE_PARAM_MMPS_UPDATE = 5,
+ RATE_PARAM_FIXED_HE_LTF = 7,
+ RATE_PARAM_FIXED_MCS,
+ RATE_PARAM_FIXED_GI = 11,
+ RATE_PARAM_AUTO = 20,
+};
+
+enum {
+ BF_SOUNDING_ON = 1,
+ BF_HW_EN_UPDATE = 17,
+ BF_MOD_EN_CTRL = 20,
+};
+
+enum {
+ CMD_BAND_NONE,
+ CMD_BAND_24G,
+ CMD_BAND_5G,
+ CMD_BAND_6G,
+};
+
+struct bss_req_hdr {
+ u8 bss_idx;
+ u8 __rsv[3];
+} __packed;
+
+enum {
+ UNI_CHANNEL_SWITCH,
+ UNI_CHANNEL_RX_PATH,
+};
+
+#define MT7996_BSS_UPDATE_MAX_SIZE (sizeof(struct bss_req_hdr) + \
+ sizeof(struct mt76_connac_bss_basic_tlv) + \
+ sizeof(struct bss_rlm_tlv) + \
+ sizeof(struct bss_ra_tlv) + \
+ sizeof(struct bss_info_uni_he) + \
+ sizeof(struct bss_rate_tlv) + \
+ sizeof(struct bss_txcmd_tlv) + \
+ sizeof(struct bss_power_save) + \
+ sizeof(struct bss_sec_tlv) + \
+ sizeof(struct bss_mld_tlv))
+
+#define MT7996_STA_UPDATE_MAX_SIZE (sizeof(struct sta_req_hdr) + \
+ sizeof(struct sta_rec_basic) + \
+ sizeof(struct sta_rec_bf) + \
+ sizeof(struct sta_rec_ht) + \
+ sizeof(struct sta_rec_he_v2) + \
+ sizeof(struct sta_rec_ba_uni) + \
+ sizeof(struct sta_rec_vht) + \
+ sizeof(struct sta_rec_uapsd) + \
+ sizeof(struct sta_rec_amsdu) + \
+ sizeof(struct sta_rec_bfee) + \
+ sizeof(struct sta_rec_phy) + \
+ sizeof(struct sta_rec_ra) + \
+ sizeof(struct sta_rec_sec) + \
+ sizeof(struct sta_rec_ra_fixed) + \
+ sizeof(struct sta_rec_he_6g_capa) + \
+ sizeof(struct sta_rec_hdrt) + \
+ sizeof(struct sta_rec_hdr_trans) + \
+ sizeof(struct tlv))
+
+#define MT7996_BEACON_UPDATE_SIZE (sizeof(struct bss_req_hdr) + \
+ sizeof(struct bss_bcn_content_tlv) + \
+ sizeof(struct bss_bcn_cntdwn_tlv) + \
+ sizeof(struct bss_bcn_mbss_tlv))
+
+#define MT7996_INBAND_FRAME_SIZE (sizeof(struct bss_req_hdr) + \
+ sizeof(struct bss_inband_discovery_tlv))
+
+enum {
+ UNI_BAND_CONFIG_RADIO_ENABLE,
+ UNI_BAND_CONFIG_RTS_THRESHOLD = 0x08,
+};
+
+enum {
+ UNI_WSYS_CONFIG_FW_LOG_CTRL,
+ UNI_WSYS_CONFIG_FW_DBG_CTRL,
+};
+
+enum {
+ UNI_RDD_CTRL_PARM,
+ UNI_RDD_CTRL_SET_TH = 0x3,
+};
+
+enum {
+ UNI_EFUSE_ACCESS = 1,
+ UNI_EFUSE_BUFFER_MODE,
+ UNI_EFUSE_FREE_BLOCK,
+ UNI_EFUSE_BUFFER_RD,
+};
+
+enum {
+ UNI_VOW_DRR_CTRL,
+ UNI_VOW_RX_AT_AIRTIME_EN = 0x0b,
+ UNI_VOW_RX_AT_AIRTIME_CLR_EN = 0x0e,
+};
+
+enum {
+ UNI_CMD_MIB_DATA,
+};
+
+enum {
+ UNI_POWER_OFF,
+};
+
+enum {
+ UNI_CMD_TWT_ARGT_UPDATE = 0x0,
+ UNI_CMD_TWT_MGMT_OFFLOAD,
+};
+
+enum {
+ UNI_RRO_DEL_ENTRY = 0x1,
+ UNI_RRO_SET_PLATFORM_TYPE,
+ UNI_RRO_GET_BA_SESSION_TABLE,
+ UNI_RRO_SET_BYPASS_MODE,
+ UNI_RRO_SET_TXFREE_PATH,
+};
+
+enum{
+ UNI_CMD_SR_ENABLE = 0x1,
+ UNI_CMD_SR_ENABLE_SD,
+ UNI_CMD_SR_ENABLE_MODE,
+ UNI_CMD_SR_ENABLE_DPD = 0x12,
+ UNI_CMD_SR_ENABLE_TX,
+ UNI_CMD_SR_SET_SRG_BITMAP = 0x80,
+ UNI_CMD_SR_SET_PARAM = 0xc1,
+ UNI_CMD_SR_SET_SIGA = 0xd0,
+};
+
+enum {
+ UNI_CMD_ACCESS_REG_BASIC = 0x0,
+ UNI_CMD_ACCESS_RF_REG_BASIC,
+};
+
+enum {
+ UNI_CMD_SER_QUERY = 0x0,
+ UNI_CMD_SER_SET = 0x2,
+ UNI_CMD_SER_TRIGGER = 0x3,
+};
+
+enum {
+ SER_QUERY,
+ /* recovery */
+ SER_SET_RECOVER_L1,
+ SER_SET_RECOVER_L2,
+ SER_SET_RECOVER_L3_RX_ABORT,
+ SER_SET_RECOVER_L3_TX_ABORT,
+ SER_SET_RECOVER_L3_TX_DISABLE,
+ SER_SET_RECOVER_L3_BF,
+ /* action */
+ SER_ENABLE = 2,
+ SER_RECOVER
+};
+
+enum {
+ MT7996_SEC_MODE_PLAIN,
+ MT7996_SEC_MODE_AES,
+ MT7996_SEC_MODE_SCRAMBLE,
+ MT7996_SEC_MODE_MAX,
+};
+
+#define MT7996_PATCH_SEC GENMASK(31, 24)
+#define MT7996_PATCH_SCRAMBLE_KEY GENMASK(15, 8)
+#define MT7996_PATCH_AES_KEY GENMASK(7, 0)
+
+#define MT7996_SEC_ENCRYPT BIT(0)
+#define MT7996_SEC_KEY_IDX GENMASK(2, 1)
+#define MT7996_SEC_IV BIT(3)
+
+#endif
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c
new file mode 100644
index 000000000000..521769eb6b0e
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c
@@ -0,0 +1,386 @@
+// SPDX-License-Identifier: ISC
+/*
+ * Copyright (C) 2022 MediaTek Inc.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+
+#include "mt7996.h"
+#include "mac.h"
+#include "../trace.h"
+
+static const struct __base mt7996_reg_base[] = {
+ [WF_AGG_BASE] = { { 0x820e2000, 0x820f2000, 0x830e2000 } },
+ [WF_ARB_BASE] = { { 0x820e3000, 0x820f3000, 0x830e3000 } },
+ [WF_TMAC_BASE] = { { 0x820e4000, 0x820f4000, 0x830e4000 } },
+ [WF_RMAC_BASE] = { { 0x820e5000, 0x820f5000, 0x830e5000 } },
+ [WF_DMA_BASE] = { { 0x820e7000, 0x820f7000, 0x830e7000 } },
+ [WF_WTBLOFF_BASE] = { { 0x820e9000, 0x820f9000, 0x830e9000 } },
+ [WF_ETBF_BASE] = { { 0x820ea000, 0x820fa000, 0x830ea000 } },
+ [WF_LPON_BASE] = { { 0x820eb000, 0x820fb000, 0x830eb000 } },
+ [WF_MIB_BASE] = { { 0x820ed000, 0x820fd000, 0x830ed000 } },
+};
+
+static const struct __map mt7996_reg_map[] = {
+ { 0x54000000, 0x02000, 0x1000 }, /* WFDMA_0 (PCIE0 MCU DMA0) */
+ { 0x55000000, 0x03000, 0x1000 }, /* WFDMA_1 (PCIE0 MCU DMA1) */
+ { 0x56000000, 0x04000, 0x1000 }, /* WFDMA reserved */
+ { 0x57000000, 0x05000, 0x1000 }, /* WFDMA MCU wrap CR */
+ { 0x58000000, 0x06000, 0x1000 }, /* WFDMA PCIE1 MCU DMA0 (MEM_DMA) */
+ { 0x59000000, 0x07000, 0x1000 }, /* WFDMA PCIE1 MCU DMA1 */
+ { 0x820c0000, 0x08000, 0x4000 }, /* WF_UMAC_TOP (PLE) */
+ { 0x820c8000, 0x0c000, 0x2000 }, /* WF_UMAC_TOP (PSE) */
+ { 0x820cc000, 0x0e000, 0x1000 }, /* WF_UMAC_TOP (PP) */
+ { 0x74030000, 0x10000, 0x1000 }, /* PCIe MAC */
+ { 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) */
+ { 0x40000000, 0x70000, 0x10000 }, /* WF_UMAC_SYSRAM */
+ { 0x00400000, 0x80000, 0x10000 }, /* WF_MCU_SYSRAM */
+ { 0x00410000, 0x90000, 0x10000 }, /* WF_MCU_SYSRAM (configure register) */
+ { 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 BN1 (WF_MUCOP) */
+ { 0x820b0000, 0xae000, 0x1000 }, /* [APB2] WFSYS_ON */
+ { 0x80020000, 0xb0000, 0x10000 }, /* WF_TOP_MISC_OFF */
+ { 0x81020000, 0xc0000, 0x10000 }, /* WF_TOP_MISC_ON */
+ { 0x7c020000, 0xd0000, 0x10000 }, /* CONN_INFRA, wfdma */
+ { 0x7c060000, 0xe0000, 0x10000 }, /* CONN_INFRA, conn_host_csr_top */
+ { 0x7c000000, 0xf0000, 0x10000 }, /* CONN_INFRA */
+ { 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);
+
+ dev->reg_l1_backup = dev->bus_ops->rr(&dev->mt76, MT_HIF_REMAP_L1);
+ dev->bus_ops->rmw(&dev->mt76, MT_HIF_REMAP_L1,
+ MT_HIF_REMAP_L1_MASK,
+ FIELD_PREP(MT_HIF_REMAP_L1_MASK, base));
+ /* use read to push write */
+ dev->bus_ops->rr(&dev->mt76, MT_HIF_REMAP_L1);
+
+ return MT_HIF_REMAP_BASE_L1 + offset;
+}
+
+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);
+
+ dev->reg_l2_backup = dev->bus_ops->rr(&dev->mt76, MT_HIF_REMAP_L2);
+ dev->bus_ops->rmw(&dev->mt76, MT_HIF_REMAP_L2,
+ MT_HIF_REMAP_L2_MASK,
+ FIELD_PREP(MT_HIF_REMAP_L2_MASK, base));
+ /* use read to push write */
+ dev->bus_ops->rr(&dev->mt76, MT_HIF_REMAP_L2);
+
+ return MT_HIF_REMAP_BASE_L2 + offset;
+}
+
+static void mt7996_reg_remap_restore(struct mt7996_dev *dev)
+{
+ /* remap to ori status */
+ if (unlikely(dev->reg_l1_backup)) {
+ dev->bus_ops->wr(&dev->mt76, MT_HIF_REMAP_L1, dev->reg_l1_backup);
+ dev->reg_l1_backup = 0;
+ }
+
+ if (dev->reg_l2_backup) {
+ dev->bus_ops->wr(&dev->mt76, MT_HIF_REMAP_L2, dev->reg_l2_backup);
+ dev->reg_l2_backup = 0;
+ }
+}
+
+static u32 __mt7996_reg_addr(struct mt7996_dev *dev, u32 addr)
+{
+ int i;
+
+ mt7996_reg_remap_restore(dev);
+
+ if (addr < 0x100000)
+ return addr;
+
+ for (i = 0; i < dev->reg.map_size; i++) {
+ u32 ofs;
+
+ if (addr < dev->reg.map[i].phys)
+ continue;
+
+ ofs = addr - dev->reg.map[i].phys;
+ if (ofs > dev->reg.map[i].size)
+ continue;
+
+ return dev->reg.map[i].mapped + ofs;
+ }
+
+ if ((addr >= MT_INFRA_BASE && addr < MT_WFSYS0_PHY_START) ||
+ (addr >= MT_WFSYS0_PHY_START && addr < MT_WFSYS1_PHY_START) ||
+ (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 && addr <= MT_CBTOP2_PHY_END)))
+ 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);
+ }
+
+ return mt7996_reg_map_l2(dev, addr);
+}
+
+static u32 mt7996_rr(struct mt76_dev *mdev, u32 offset)
+{
+ struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
+
+ return dev->bus_ops->rr(mdev, __mt7996_reg_addr(dev, offset));
+}
+
+static void mt7996_wr(struct mt76_dev *mdev, u32 offset, u32 val)
+{
+ struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
+
+ dev->bus_ops->wr(mdev, __mt7996_reg_addr(dev, offset), val);
+}
+
+static u32 mt7996_rmw(struct mt76_dev *mdev, u32 offset, u32 mask, u32 val)
+{
+ struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
+
+ return dev->bus_ops->rmw(mdev, __mt7996_reg_addr(dev, offset), mask, val);
+}
+
+static int mt7996_mmio_init(struct mt76_dev *mdev,
+ void __iomem *mem_base,
+ u32 device_id)
+{
+ struct mt76_bus_ops *bus_ops;
+ struct mt7996_dev *dev;
+
+ dev = container_of(mdev, struct mt7996_dev, mt76);
+ mt76_mmio_init(&dev->mt76, mem_base);
+
+ switch (device_id) {
+ case 0x7990:
+ dev->reg.base = mt7996_reg_base;
+ dev->reg.map = mt7996_reg_map;
+ dev->reg.map_size = ARRAY_SIZE(mt7996_reg_map);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dev->bus_ops = dev->mt76.bus;
+ bus_ops = devm_kmemdup(dev->mt76.dev, dev->bus_ops, sizeof(*bus_ops),
+ GFP_KERNEL);
+ if (!bus_ops)
+ return -ENOMEM;
+
+ bus_ops->rr = mt7996_rr;
+ bus_ops->wr = mt7996_wr;
+ bus_ops->rmw = mt7996_rmw;
+ dev->mt76.bus = bus_ops;
+
+ mdev->rev = (device_id << 16) | (mt76_rr(dev, MT_HW_REV) & 0xff);
+
+ dev_dbg(mdev->dev, "ASIC revision: %04x\n", mdev->rev);
+
+ return 0;
+}
+
+void mt7996_dual_hif_set_irq_mask(struct mt7996_dev *dev, bool write_reg,
+ u32 clear, u32 set)
+{
+ struct mt76_dev *mdev = &dev->mt76;
+ unsigned long flags;
+
+ spin_lock_irqsave(&mdev->mmio.irq_lock, flags);
+
+ mdev->mmio.irqmask &= ~clear;
+ mdev->mmio.irqmask |= set;
+
+ if (write_reg) {
+ mt76_wr(dev, MT_INT_MASK_CSR, mdev->mmio.irqmask);
+ mt76_wr(dev, MT_INT1_MASK_CSR, mdev->mmio.irqmask);
+ }
+
+ spin_unlock_irqrestore(&mdev->mmio.irq_lock, flags);
+}
+
+static void mt7996_rx_poll_complete(struct mt76_dev *mdev,
+ enum mt76_rxq_id q)
+{
+ struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
+
+ mt7996_irq_enable(dev, MT_INT_RX(q));
+}
+
+/* TODO: support 2/4/6/8 MSI-X vectors */
+static void mt7996_irq_tasklet(struct tasklet_struct *t)
+{
+ struct mt7996_dev *dev = from_tasklet(dev, t, irq_tasklet);
+ u32 i, intr, mask, intr1;
+
+ mt76_wr(dev, MT_INT_MASK_CSR, 0);
+ if (dev->hif2)
+ mt76_wr(dev, MT_INT1_MASK_CSR, 0);
+
+ intr = mt76_rr(dev, MT_INT_SOURCE_CSR);
+ intr &= dev->mt76.mmio.irqmask;
+ mt76_wr(dev, MT_INT_SOURCE_CSR, intr);
+
+ if (dev->hif2) {
+ intr1 = mt76_rr(dev, MT_INT1_SOURCE_CSR);
+ intr1 &= dev->mt76.mmio.irqmask;
+ mt76_wr(dev, MT_INT1_SOURCE_CSR, intr1);
+
+ intr |= intr1;
+ }
+
+ trace_dev_irq(&dev->mt76, intr, dev->mt76.mmio.irqmask);
+
+ mask = intr & MT_INT_RX_DONE_ALL;
+ if (intr & MT_INT_TX_DONE_MCU)
+ mask |= MT_INT_TX_DONE_MCU;
+ mt7996_irq_disable(dev, mask);
+
+ if (intr & MT_INT_TX_DONE_MCU)
+ napi_schedule(&dev->mt76.tx_napi);
+
+ for (i = 0; i < __MT_RXQ_MAX; i++) {
+ if ((intr & MT_INT_RX(i)))
+ napi_schedule(&dev->mt76.napi[i]);
+ }
+
+ if (intr & MT_INT_MCU_CMD) {
+ u32 val = mt76_rr(dev, MT_MCU_CMD);
+
+ mt76_wr(dev, MT_MCU_CMD, val);
+ if (val & MT_MCU_CMD_ERROR_MASK) {
+ dev->reset_state = val;
+ ieee80211_queue_work(mt76_hw(dev), &dev->reset_work);
+ wake_up(&dev->reset_wait);
+ }
+ }
+}
+
+irqreturn_t mt7996_irq_handler(int irq, void *dev_instance)
+{
+ struct mt7996_dev *dev = dev_instance;
+
+ mt76_wr(dev, MT_INT_MASK_CSR, 0);
+ if (dev->hif2)
+ mt76_wr(dev, MT_INT1_MASK_CSR, 0);
+
+ if (!test_bit(MT76_STATE_INITIALIZED, &dev->mphy.state))
+ return IRQ_NONE;
+
+ tasklet_schedule(&dev->irq_tasklet);
+
+ return IRQ_HANDLED;
+}
+
+struct mt7996_dev *mt7996_mmio_probe(struct device *pdev,
+ void __iomem *mem_base, u32 device_id)
+{
+ static const struct mt76_driver_ops drv_ops = {
+ /* txwi_size = txd size + txp size */
+ .txwi_size = MT_TXD_SIZE + sizeof(struct mt7996_txp),
+ .drv_flags = MT_DRV_TXWI_NO_FREE |
+ MT_DRV_HW_MGMT_TXQ,
+ .survey_flags = SURVEY_INFO_TIME_TX |
+ SURVEY_INFO_TIME_RX |
+ SURVEY_INFO_TIME_BSS_RX,
+ .token_size = MT7996_TOKEN_SIZE,
+ .tx_prepare_skb = mt7996_tx_prepare_skb,
+ .tx_complete_skb = mt7996_tx_complete_skb,
+ .rx_skb = mt7996_queue_rx_skb,
+ .rx_check = mt7996_rx_check,
+ .rx_poll_complete = mt7996_rx_poll_complete,
+ .sta_ps = mt7996_sta_ps,
+ .sta_add = mt7996_mac_sta_add,
+ .sta_remove = mt7996_mac_sta_remove,
+ .update_survey = mt7996_update_channel,
+ };
+ struct mt7996_dev *dev;
+ struct mt76_dev *mdev;
+ int ret;
+
+ mdev = mt76_alloc_device(pdev, sizeof(*dev), &mt7996_ops, &drv_ops);
+ if (!mdev)
+ return ERR_PTR(-ENOMEM);
+
+ dev = container_of(mdev, struct mt7996_dev, mt76);
+
+ ret = mt7996_mmio_init(mdev, mem_base, device_id);
+ if (ret)
+ goto error;
+
+ tasklet_setup(&dev->irq_tasklet, mt7996_irq_tasklet);
+
+ mt76_wr(dev, MT_INT_MASK_CSR, 0);
+
+ return dev;
+
+error:
+ mt76_free_device(&dev->mt76);
+
+ return ERR_PTR(ret);
+}
+
+static int __init mt7996_init(void)
+{
+ int ret;
+
+ ret = pci_register_driver(&mt7996_hif_driver);
+ if (ret)
+ return ret;
+
+ ret = pci_register_driver(&mt7996_pci_driver);
+ if (ret)
+ pci_unregister_driver(&mt7996_hif_driver);
+
+ return ret;
+}
+
+static void __exit mt7996_exit(void)
+{
+ pci_unregister_driver(&mt7996_pci_driver);
+ pci_unregister_driver(&mt7996_hif_driver);
+}
+
+module_init(mt7996_init);
+module_exit(mt7996_exit);
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h
new file mode 100644
index 000000000000..725344791b4c
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h
@@ -0,0 +1,523 @@
+/* SPDX-License-Identifier: ISC */
+/*
+ * Copyright (C) 2022 MediaTek Inc.
+ */
+
+#ifndef __MT7996_H
+#define __MT7996_H
+
+#include <linux/interrupt.h>
+#include <linux/ktime.h>
+#include "../mt76_connac.h"
+#include "regs.h"
+
+#define MT7996_MAX_INTERFACES 19
+#define MT7996_MAX_WMM_SETS 4
+#define MT7996_WTBL_SIZE 544
+#define MT7996_WTBL_RESERVED (MT7996_WTBL_SIZE - 1)
+#define MT7996_WTBL_STA (MT7996_WTBL_RESERVED - \
+ MT7996_MAX_INTERFACES)
+
+#define MT7996_WATCHDOG_TIME (HZ / 10)
+#define MT7996_RESET_TIMEOUT (30 * HZ)
+
+#define MT7996_TX_RING_SIZE 2048
+#define MT7996_TX_MCU_RING_SIZE 256
+#define MT7996_TX_FWDL_RING_SIZE 128
+
+#define MT7996_RX_RING_SIZE 1536
+#define MT7996_RX_MCU_RING_SIZE 512
+
+#define MT7996_FIRMWARE_WA "mediatek/mt7996/mt7996_wa.bin"
+#define MT7996_FIRMWARE_WM "mediatek/mt7996/mt7996_wm.bin"
+#define MT7996_ROM_PATCH "mediatek/mt7996/mt7996_rom_patch.bin"
+
+#define MT7996_EEPROM_DEFAULT "mediatek/mt7996/mt7996_eeprom.bin"
+#define MT7996_EEPROM_SIZE 7680
+#define MT7996_EEPROM_BLOCK_SIZE 16
+#define MT7996_TOKEN_SIZE 8192
+
+#define MT7996_CFEND_RATE_DEFAULT 0x49 /* OFDM 24M */
+#define MT7996_CFEND_RATE_11B 0x03 /* 11B LP, 11M */
+
+#define MT7996_MAX_TWT_AGRT 16
+#define MT7996_MAX_STA_TWT_AGRT 8
+#define MT7996_MAX_QUEUE (__MT_RXQ_MAX + __MT_MCUQ_MAX + 3)
+
+struct mt7996_vif;
+struct mt7996_sta;
+struct mt7996_dfs_pulse;
+struct mt7996_dfs_pattern;
+
+enum mt7996_txq_id {
+ MT7996_TXQ_FWDL = 16,
+ MT7996_TXQ_MCU_WM,
+ MT7996_TXQ_BAND0,
+ MT7996_TXQ_BAND1,
+ MT7996_TXQ_MCU_WA,
+ MT7996_TXQ_BAND2,
+};
+
+enum mt7996_rxq_id {
+ MT7996_RXQ_MCU_WM = 0,
+ MT7996_RXQ_MCU_WA,
+ MT7996_RXQ_MCU_WA_MAIN = 2,
+ MT7996_RXQ_MCU_WA_EXT = 2,/* unused */
+ MT7996_RXQ_MCU_WA_TRI = 3,
+ MT7996_RXQ_BAND0 = 4,
+ MT7996_RXQ_BAND1 = 4,/* unused */
+ MT7996_RXQ_BAND2 = 5,
+};
+
+struct mt7996_twt_flow {
+ struct list_head list;
+ u64 start_tsf;
+ u64 tsf;
+ u32 duration;
+ u16 wcid;
+ __le16 mantissa;
+ u8 exp;
+ u8 table_id;
+ u8 id;
+ u8 protection:1;
+ u8 flowtype:1;
+ u8 trigger:1;
+ u8 sched:1;
+};
+
+DECLARE_EWMA(avg_signal, 10, 8)
+
+struct mt7996_sta {
+ struct mt76_wcid wcid; /* must be first */
+
+ struct mt7996_vif *vif;
+
+ struct list_head poll_list;
+ struct list_head rc_list;
+ u32 airtime_ac[8];
+
+ int ack_signal;
+ struct ewma_avg_signal avg_ack_signal;
+
+ unsigned long changed;
+ unsigned long jiffies;
+ unsigned long ampdu_state;
+
+ struct mt76_sta_stats stats;
+
+ struct mt76_connac_sta_key_conf bip;
+
+ struct {
+ u8 flowid_mask;
+ struct mt7996_twt_flow flow[MT7996_MAX_STA_TWT_AGRT];
+ } twt;
+};
+
+struct mt7996_vif_cap {
+ bool ht_ldpc:1;
+ bool vht_ldpc:1;
+ bool he_ldpc:1;
+ bool vht_su_ebfer:1;
+ bool vht_su_ebfee:1;
+ bool vht_mu_ebfer:1;
+ bool vht_mu_ebfee:1;
+ bool he_su_ebfer:1;
+ bool he_su_ebfee:1;
+ bool he_mu_ebfer:1;
+};
+
+struct mt7996_vif {
+ struct mt76_vif mt76; /* must be first */
+
+ struct mt7996_vif_cap cap;
+ struct mt7996_sta sta;
+ struct mt7996_phy *phy;
+
+ struct ieee80211_tx_queue_params queue_params[IEEE80211_NUM_ACS];
+ struct cfg80211_bitrate_mask bitrate_mask;
+};
+
+/* per-phy stats. */
+struct mib_stats {
+ u32 ack_fail_cnt;
+ u32 fcs_err_cnt;
+ u32 rts_cnt;
+ u32 rts_retries_cnt;
+ u32 ba_miss_cnt;
+ u32 tx_mu_bf_cnt;
+ u32 tx_mu_mpdu_cnt;
+ u32 tx_mu_acked_mpdu_cnt;
+ u32 tx_su_acked_mpdu_cnt;
+ u32 tx_bf_ibf_ppdu_cnt;
+ u32 tx_bf_ebf_ppdu_cnt;
+
+ u32 tx_bf_rx_fb_all_cnt;
+ u32 tx_bf_rx_fb_eht_cnt;
+ u32 tx_bf_rx_fb_he_cnt;
+ u32 tx_bf_rx_fb_vht_cnt;
+ u32 tx_bf_rx_fb_ht_cnt;
+
+ u32 tx_bf_rx_fb_bw; /* value of last sample, not cumulative */
+ u32 tx_bf_rx_fb_nc_cnt;
+ u32 tx_bf_rx_fb_nr_cnt;
+ u32 tx_bf_fb_cpl_cnt;
+ u32 tx_bf_fb_trig_cnt;
+
+ u32 tx_ampdu_cnt;
+ u32 tx_stop_q_empty_cnt;
+ u32 tx_mpdu_attempts_cnt;
+ u32 tx_mpdu_success_cnt;
+ /* BF counter is PPDU-based, so remove MPDU-based BF counter */
+
+ u32 tx_rwp_fail_cnt;
+ u32 tx_rwp_need_cnt;
+
+ /* rx stats */
+ u32 rx_fifo_full_cnt;
+ u32 channel_idle_cnt;
+ u32 rx_vector_mismatch_cnt;
+ u32 rx_delimiter_fail_cnt;
+ u32 rx_len_mismatch_cnt;
+ u32 rx_mpdu_cnt;
+ u32 rx_ampdu_cnt;
+ u32 rx_ampdu_bytes_cnt;
+ u32 rx_ampdu_valid_subframe_cnt;
+ u32 rx_ampdu_valid_subframe_bytes_cnt;
+ u32 rx_pfdrop_cnt;
+ u32 rx_vec_queue_overflow_drop_cnt;
+ u32 rx_ba_cnt;
+
+ u32 tx_amsdu[8];
+ u32 tx_amsdu_cnt;
+};
+
+struct mt7996_hif {
+ struct list_head list;
+
+ struct device *dev;
+ void __iomem *regs;
+ int irq;
+};
+
+struct mt7996_phy {
+ struct mt76_phy *mt76;
+ struct mt7996_dev *dev;
+
+ struct ieee80211_sband_iftype_data iftype[NUM_NL80211_BANDS][NUM_NL80211_IFTYPES];
+
+ struct ieee80211_vif *monitor_vif;
+
+ u32 rxfilter;
+ u64 omac_mask;
+
+ u16 noise;
+
+ s16 coverage_class;
+ u8 slottime;
+
+ u8 rdd_state;
+
+ u32 rx_ampdu_ts;
+ u32 ampdu_ref;
+
+ struct mib_stats mib;
+ struct mt76_channel_state state_ts;
+};
+
+struct mt7996_dev {
+ union { /* must be first */
+ struct mt76_dev mt76;
+ struct mt76_phy mphy;
+ };
+
+ struct mt7996_hif *hif2;
+ struct mt7996_reg_desc reg;
+ u8 q_id[MT7996_MAX_QUEUE];
+ u32 q_int_mask[MT7996_MAX_QUEUE];
+ u32 q_wfdma_mask;
+
+ const struct mt76_bus_ops *bus_ops;
+ struct tasklet_struct irq_tasklet;
+ struct mt7996_phy phy;
+
+ /* monitor rx chain configured channel */
+ struct cfg80211_chan_def rdd2_chandef;
+ struct mt7996_phy *rdd2_phy;
+
+ u16 chainmask;
+ u8 chainshift[__MT_MAX_BAND];
+ u32 hif_idx;
+
+ struct work_struct init_work;
+ struct work_struct rc_work;
+ struct work_struct reset_work;
+ wait_queue_head_t reset_wait;
+ u32 reset_state;
+
+ struct list_head sta_rc_list;
+ struct list_head sta_poll_list;
+ struct list_head twt_list;
+ spinlock_t sta_poll_lock;
+
+ u32 hw_pattern;
+
+ bool dbdc_support:1;
+ bool tbtc_support:1;
+ bool flash_mode:1;
+
+ bool ibf;
+ u8 fw_debug_wm;
+ u8 fw_debug_wa;
+ u8 fw_debug_bin;
+ u16 fw_debug_seq;
+
+ struct dentry *debugfs_dir;
+ struct rchan *relay_fwlog;
+
+ struct {
+ u8 table_mask;
+ u8 n_agrt;
+ } twt;
+
+ u32 reg_l1_backup;
+ u32 reg_l2_backup;
+};
+
+enum {
+ WFDMA0 = 0x0,
+ WFDMA1,
+ WFDMA_EXT,
+ __MT_WFDMA_MAX,
+};
+
+enum {
+ MT_CTX0,
+ MT_HIF0 = 0x0,
+
+ MT_LMAC_AC00 = 0x0,
+ MT_LMAC_AC01,
+ MT_LMAC_AC02,
+ MT_LMAC_AC03,
+ MT_LMAC_ALTX0 = 0x10,
+ MT_LMAC_BMC0,
+ MT_LMAC_BCN0,
+ MT_LMAC_PSMP0,
+};
+
+enum {
+ MT_RX_SEL0,
+ MT_RX_SEL1,
+ MT_RX_SEL2, /* monitor chain */
+};
+
+enum mt7996_rdd_cmd {
+ RDD_STOP,
+ RDD_START,
+ RDD_DET_MODE,
+ RDD_RADAR_EMULATE,
+ RDD_START_TXQ = 20,
+ RDD_CAC_START = 50,
+ RDD_CAC_END,
+ RDD_NORMAL_START,
+ RDD_DISABLE_DFS_CAL,
+ RDD_PULSE_DBG,
+ RDD_READ_PULSE,
+ RDD_RESUME_BF,
+ RDD_IRQ_OFF,
+};
+
+static inline struct mt7996_phy *
+mt7996_hw_phy(struct ieee80211_hw *hw)
+{
+ struct mt76_phy *phy = hw->priv;
+
+ return phy->priv;
+}
+
+static inline struct mt7996_dev *
+mt7996_hw_dev(struct ieee80211_hw *hw)
+{
+ struct mt76_phy *phy = hw->priv;
+
+ return container_of(phy->dev, struct mt7996_dev, mt76);
+}
+
+static inline struct mt7996_phy *
+__mt7996_phy(struct mt7996_dev *dev, enum mt76_band_id band)
+{
+ struct mt76_phy *phy = dev->mt76.phys[band];
+
+ if (!phy)
+ return NULL;
+
+ return phy->priv;
+}
+
+static inline struct mt7996_phy *
+mt7996_phy2(struct mt7996_dev *dev)
+{
+ return __mt7996_phy(dev, MT_BAND1);
+}
+
+static inline struct mt7996_phy *
+mt7996_phy3(struct mt7996_dev *dev)
+{
+ return __mt7996_phy(dev, MT_BAND2);
+}
+
+extern const struct ieee80211_ops mt7996_ops;
+extern struct pci_driver mt7996_pci_driver;
+extern struct pci_driver mt7996_hif_driver;
+
+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);
+int mt7996_register_device(struct mt7996_dev *dev);
+void mt7996_unregister_device(struct mt7996_dev *dev);
+int mt7996_eeprom_init(struct mt7996_dev *dev);
+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);
+int mt7996_dma_init(struct mt7996_dev *dev);
+void mt7996_dma_prefetch(struct mt7996_dev *dev);
+void mt7996_dma_cleanup(struct mt7996_dev *dev);
+int mt7996_mcu_init(struct mt7996_dev *dev);
+int mt7996_mcu_twt_agrt_update(struct mt7996_dev *dev,
+ struct mt7996_vif *mvif,
+ struct mt7996_twt_flow *flow,
+ int cmd);
+int mt7996_mcu_add_dev_info(struct mt7996_phy *phy,
+ struct ieee80211_vif *vif, bool enable);
+int mt7996_mcu_add_bss_info(struct mt7996_phy *phy,
+ struct ieee80211_vif *vif, int enable);
+int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, bool enable);
+int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev,
+ struct ieee80211_ampdu_params *params,
+ bool add);
+int mt7996_mcu_add_rx_ba(struct mt7996_dev *dev,
+ struct ieee80211_ampdu_params *params,
+ bool add);
+int mt7996_mcu_update_bss_color(struct mt7996_dev *dev, struct ieee80211_vif *vif,
+ struct cfg80211_he_bss_color *he_bss_color);
+int mt7996_mcu_add_beacon(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ int enable);
+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_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_set_channel(struct mt7996_phy *phy);
+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);
+int mt7996_mcu_set_fixed_rate_ctrl(struct mt7996_dev *dev,
+ void *data, u16 version);
+int mt7996_mcu_set_eeprom(struct mt7996_dev *dev);
+int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset);
+int mt7996_mcu_get_eeprom_free_block(struct mt7996_dev *dev, u8 *block_num);
+int mt7996_mcu_set_ser(struct mt7996_dev *dev, u8 action, u8 set, u8 band);
+int mt7996_mcu_set_txbf(struct mt7996_dev *dev, u8 action);
+int mt7996_mcu_set_fcc5_lpn(struct mt7996_dev *dev, int val);
+int mt7996_mcu_set_pulse_th(struct mt7996_dev *dev,
+ const struct mt7996_dfs_pulse *pulse);
+int mt7996_mcu_set_radar_th(struct mt7996_dev *dev, int index,
+ const struct mt7996_dfs_pattern *pattern);
+int mt7996_mcu_set_radio_en(struct mt7996_phy *phy, bool enable);
+void mt7996_mcu_set_pm(void *priv, u8 *mac, struct ieee80211_vif *vif);
+int mt7996_mcu_set_rts_thresh(struct mt7996_phy *phy, u32 val);
+int mt7996_mcu_get_chan_mib_info(struct mt7996_phy *phy, bool chan_switch);
+int mt7996_mcu_rdd_cmd(struct mt7996_dev *dev, int cmd, u8 index,
+ u8 rx_sel, u8 val);
+int mt7996_mcu_rdd_background_enable(struct mt7996_phy *phy,
+ struct cfg80211_chan_def *chandef);
+int mt7996_mcu_rf_regval(struct mt7996_dev *dev, u32 regidx, u32 *val, bool set);
+int mt7996_mcu_set_hdr_trans(struct mt7996_dev *dev, bool hdr_trans);
+int mt7996_mcu_set_rro(struct mt7996_dev *dev, u16 tag, u8 val);
+int mt7996_mcu_wa_cmd(struct mt7996_dev *dev, int cmd, u32 a1, u32 a2, u32 a3);
+int mt7996_mcu_fw_log_2_host(struct mt7996_dev *dev, u8 type, u8 ctrl);
+int mt7996_mcu_fw_dbg_ctrl(struct mt7996_dev *dev, u32 module, u8 level);
+void mt7996_mcu_rx_event(struct mt7996_dev *dev, struct sk_buff *skb);
+void mt7996_mcu_exit(struct mt7996_dev *dev);
+
+void mt7996_dual_hif_set_irq_mask(struct mt7996_dev *dev, bool write_reg,
+ u32 clear, u32 set);
+
+static inline void mt7996_irq_enable(struct mt7996_dev *dev, u32 mask)
+{
+ if (dev->hif2)
+ mt7996_dual_hif_set_irq_mask(dev, false, 0, mask);
+ else
+ mt76_set_irq_mask(&dev->mt76, 0, 0, mask);
+
+ tasklet_schedule(&dev->irq_tasklet);
+}
+
+static inline void mt7996_irq_disable(struct mt7996_dev *dev, u32 mask)
+{
+ if (dev->hif2)
+ mt7996_dual_hif_set_irq_mask(dev, true, mask, 0);
+ else
+ mt76_set_irq_mask(&dev->mt76, MT_INT_MASK_CSR, mask, 0);
+}
+
+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, int pid,
+ struct ieee80211_key_conf *key, u32 changed);
+void mt7996_mac_set_timing(struct mt7996_phy *phy);
+int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta);
+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_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,
+ u8 flowid);
+void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw,
+ struct ieee80211_sta *sta,
+ struct ieee80211_twt_setup *twt);
+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);
+void mt7996_tx_complete_skb(struct mt76_dev *mdev, struct mt76_queue_entry *e);
+void mt7996_tx_token_put(struct mt7996_dev *dev);
+void mt7996_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
+ struct sk_buff *skb, u32 *info);
+bool mt7996_rx_check(struct mt76_dev *mdev, void *data, int len);
+void mt7996_sta_ps(struct mt76_dev *mdev, struct ieee80211_sta *sta, bool ps);
+void mt7996_stats_work(struct work_struct *work);
+int mt76_dfs_start_rdd(struct mt7996_dev *dev, bool force);
+int mt7996_dfs_init_radar_detector(struct mt7996_phy *phy);
+void mt7996_set_stream_he_caps(struct mt7996_phy *phy);
+void mt7996_set_stream_vht_txbf_caps(struct mt7996_phy *phy);
+void mt7996_update_channel(struct mt76_phy *mphy);
+int mt7996_init_debugfs(struct mt7996_phy *phy);
+void mt7996_debugfs_rx_fw_monitor(struct mt7996_dev *dev, const void *data, int len);
+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 mt76_connac_sta_key_conf *sta_key_conf,
+ struct ieee80211_key_conf *key, int mcu_cmd,
+ struct mt76_wcid *wcid, enum set_key_cmd cmd);
+int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta);
+#ifdef CONFIG_MAC80211_DEBUGFS
+void mt7996_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, struct dentry *dir);
+#endif
+
+#endif
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/pci.c b/drivers/net/wireless/mediatek/mt76/mt7996/pci.c
new file mode 100644
index 000000000000..64aee3fb5445
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/pci.c
@@ -0,0 +1,222 @@
+// SPDX-License-Identifier: ISC
+/*
+ * Copyright (C) 2022 MediaTek Inc.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+
+#include "mt7996.h"
+#include "mac.h"
+#include "../trace.h"
+
+static LIST_HEAD(hif_list);
+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) },
+ { },
+};
+
+static const struct pci_device_id mt7996_hif_device_table[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7991) },
+ { },
+};
+
+static struct mt7996_hif *mt7996_pci_get_hif2(u32 idx)
+{
+ struct mt7996_hif *hif;
+ u32 val;
+
+ spin_lock_bh(&hif_lock);
+
+ list_for_each_entry(hif, &hif_list, list) {
+ val = readl(hif->regs + MT_PCIE_RECOG_ID);
+ val &= MT_PCIE_RECOG_ID_MASK;
+ if (val != idx)
+ continue;
+
+ get_device(hif->dev);
+ goto out;
+ }
+ hif = NULL;
+
+out:
+ spin_unlock_bh(&hif_lock);
+
+ return hif;
+}
+
+static void mt7996_put_hif2(struct mt7996_hif *hif)
+{
+ if (!hif)
+ return;
+
+ put_device(hif->dev);
+}
+
+static struct mt7996_hif *mt7996_pci_init_hif2(struct pci_dev *pdev)
+{
+ hif_idx++;
+ if (!pci_get_device(PCI_VENDOR_ID_MEDIATEK, 0x7991, NULL))
+ return NULL;
+
+ writel(hif_idx | MT_PCIE_RECOG_ID_SEM,
+ pcim_iomap_table(pdev)[0] + MT_PCIE_RECOG_ID);
+
+ return mt7996_pci_get_hif2(hif_idx);
+}
+
+static int mt7996_pci_hif2_probe(struct pci_dev *pdev)
+{
+ struct mt7996_hif *hif;
+
+ hif = devm_kzalloc(&pdev->dev, sizeof(*hif), GFP_KERNEL);
+ if (!hif)
+ return -ENOMEM;
+
+ hif->dev = &pdev->dev;
+ hif->regs = pcim_iomap_table(pdev)[0];
+ hif->irq = pdev->irq;
+ spin_lock_bh(&hif_lock);
+ list_add(&hif->list, &hif_list);
+ spin_unlock_bh(&hif_lock);
+ pci_set_drvdata(pdev, hif);
+
+ return 0;
+}
+
+static int mt7996_pci_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ struct pci_dev *hif2_dev;
+ struct mt7996_dev *dev;
+ struct mt76_dev *mdev;
+ struct mt7996_hif *hif2;
+ int irq, ret;
+
+ ret = pcim_enable_device(pdev);
+ if (ret)
+ return ret;
+
+ ret = pcim_iomap_regions(pdev, BIT(0), pci_name(pdev));
+ if (ret)
+ return ret;
+
+ pci_set_master(pdev);
+
+ ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
+ if (ret)
+ return ret;
+
+ mt76_pci_disable_aspm(pdev);
+
+ if (id->device == 0x7991)
+ return mt7996_pci_hif2_probe(pdev);
+
+ dev = mt7996_mmio_probe(&pdev->dev, pcim_iomap_table(pdev)[0],
+ id->device);
+ if (IS_ERR(dev))
+ return PTR_ERR(dev);
+
+ mdev = &dev->mt76;
+ mt7996_wfsys_reset(dev);
+ hif2 = mt7996_pci_init_hif2(pdev);
+
+ ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
+ if (ret < 0)
+ goto free_device;
+
+ irq = pdev->irq;
+ ret = devm_request_irq(mdev->dev, irq, mt7996_irq_handler,
+ IRQF_SHARED, KBUILD_MODNAME, dev);
+ if (ret)
+ goto free_irq_vector;
+
+ mt76_wr(dev, MT_INT_MASK_CSR, 0);
+ /* master switch of PCIe tnterrupt enable */
+ mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff);
+
+ if (hif2) {
+ hif2_dev = container_of(hif2->dev, struct pci_dev, dev);
+ dev->hif2 = hif2;
+
+ ret = pci_alloc_irq_vectors(hif2_dev, 1, 1, PCI_IRQ_ALL_TYPES);
+ if (ret < 0)
+ goto free_hif2;
+
+ dev->hif2->irq = hif2_dev->irq;
+ ret = devm_request_irq(mdev->dev, dev->hif2->irq,
+ mt7996_irq_handler, IRQF_SHARED,
+ KBUILD_MODNAME "-hif", dev);
+ if (ret)
+ goto free_hif2_irq_vector;
+
+ mt76_wr(dev, MT_INT1_MASK_CSR, 0);
+ /* master switch of PCIe tnterrupt enable */
+ mt76_wr(dev, MT_PCIE1_MAC_INT_ENABLE, 0xff);
+ }
+
+ ret = mt7996_register_device(dev);
+ if (ret)
+ goto free_hif2_irq;
+
+ return 0;
+
+free_hif2_irq:
+ if (dev->hif2)
+ devm_free_irq(mdev->dev, dev->hif2->irq, dev);
+free_hif2_irq_vector:
+ if (dev->hif2)
+ pci_free_irq_vectors(hif2_dev);
+free_hif2:
+ if (dev->hif2)
+ put_device(dev->hif2->dev);
+ devm_free_irq(mdev->dev, irq, dev);
+free_irq_vector:
+ pci_free_irq_vectors(pdev);
+free_device:
+ mt76_free_device(&dev->mt76);
+
+ return ret;
+}
+
+static void mt7996_hif_remove(struct pci_dev *pdev)
+{
+ struct mt7996_hif *hif = pci_get_drvdata(pdev);
+
+ list_del(&hif->list);
+}
+
+static void mt7996_pci_remove(struct pci_dev *pdev)
+{
+ struct mt76_dev *mdev;
+ struct mt7996_dev *dev;
+
+ mdev = pci_get_drvdata(pdev);
+ dev = container_of(mdev, struct mt7996_dev, mt76);
+ mt7996_put_hif2(dev->hif2);
+ mt7996_unregister_device(dev);
+}
+
+struct pci_driver mt7996_hif_driver = {
+ .name = KBUILD_MODNAME "_hif",
+ .id_table = mt7996_hif_device_table,
+ .probe = mt7996_pci_probe,
+ .remove = mt7996_hif_remove,
+};
+
+struct pci_driver mt7996_pci_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = mt7996_pci_device_table,
+ .probe = mt7996_pci_probe,
+ .remove = mt7996_pci_remove,
+};
+
+MODULE_DEVICE_TABLE(pci, mt7996_pci_device_table);
+MODULE_DEVICE_TABLE(pci, mt7996_hif_device_table);
+MODULE_FIRMWARE(MT7996_FIRMWARE_WA);
+MODULE_FIRMWARE(MT7996_FIRMWARE_WM);
+MODULE_FIRMWARE(MT7996_ROM_PATCH);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/regs.h b/drivers/net/wireless/mediatek/mt76/mt7996/regs.h
new file mode 100644
index 000000000000..794f61b93a46
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/regs.h
@@ -0,0 +1,542 @@
+/* SPDX-License-Identifier: ISC */
+/*
+ * Copyright (C) 2022 MediaTek Inc.
+ */
+
+#ifndef __MT7996_REGS_H
+#define __MT7996_REGS_H
+
+struct __map {
+ u32 phys;
+ u32 mapped;
+ u32 size;
+};
+
+struct __base {
+ u32 band_base[__MT_MAX_BAND];
+};
+
+/* used to differentiate between generations */
+struct mt7996_reg_desc {
+ const struct __base *base;
+ const struct __map *map;
+ u32 map_size;
+};
+
+enum base_rev {
+ WF_AGG_BASE,
+ WF_ARB_BASE,
+ WF_TMAC_BASE,
+ WF_RMAC_BASE,
+ WF_DMA_BASE,
+ WF_WTBLOFF_BASE,
+ WF_ETBF_BASE,
+ WF_LPON_BASE,
+ WF_MIB_BASE,
+ __MT_REG_BASE_MAX,
+};
+
+#define __BASE(_id, _band) (dev->reg.base[(_id)].band_base[(_band)])
+
+#define MT_MCU_INT_EVENT 0x2108
+#define MT_MCU_INT_EVENT_DMA_STOPPED BIT(0)
+#define MT_MCU_INT_EVENT_DMA_INIT BIT(1)
+#define MT_MCU_INT_EVENT_RESET_DONE BIT(3)
+
+/* PLE */
+#define MT_PLE_BASE 0x820c0000
+#define MT_PLE(ofs) (MT_PLE_BASE + (ofs))
+
+#define MT_FL_Q_EMPTY MT_PLE(0x360)
+#define MT_FL_Q0_CTRL MT_PLE(0x3e0)
+#define MT_FL_Q2_CTRL MT_PLE(0x3e8)
+#define MT_FL_Q3_CTRL MT_PLE(0x3ec)
+
+#define MT_PLE_FREEPG_CNT MT_PLE(0x380)
+#define MT_PLE_FREEPG_HEAD_TAIL MT_PLE(0x384)
+#define MT_PLE_PG_HIF_GROUP MT_PLE(0x00c)
+#define MT_PLE_HIF_PG_INFO MT_PLE(0x388)
+
+#define MT_PLE_AC_QEMPTY(ac, n) MT_PLE(0x600 + 0x80 * (ac) + ((n) << 2))
+#define MT_PLE_AMSDU_PACK_MSDU_CNT(n) MT_PLE(0x10e0 + ((n) << 2))
+
+/* WF MDP TOP */
+#define MT_MDP_BASE 0x820cc000
+#define MT_MDP(ofs) (MT_MDP_BASE + (ofs))
+
+#define MT_MDP_DCR2 MT_MDP(0x8e8)
+#define MT_MDP_DCR2_RX_TRANS_SHORT BIT(2)
+
+/* TMAC: band 0(0x820e4000), band 1(0x820f4000), band 2(0x830e4000) */
+#define MT_WF_TMAC_BASE(_band) __BASE(WF_TMAC_BASE, (_band))
+#define MT_WF_TMAC(_band, ofs) (MT_WF_TMAC_BASE(_band) + (ofs))
+
+#define MT_TMAC_TCR0(_band) MT_WF_TMAC(_band, 0)
+#define MT_TMAC_TCR0_TX_BLINK GENMASK(7, 6)
+
+#define MT_TMAC_CDTR(_band) MT_WF_TMAC(_band, 0x0c8)
+#define MT_TMAC_ODTR(_band) MT_WF_TMAC(_band, 0x0cc)
+#define MT_TIMEOUT_VAL_PLCP GENMASK(15, 0)
+#define MT_TIMEOUT_VAL_CCA GENMASK(31, 16)
+
+#define MT_TMAC_ICR0(_band) MT_WF_TMAC(_band, 0x014)
+#define MT_IFS_EIFS_OFDM GENMASK(8, 0)
+#define MT_IFS_RIFS GENMASK(14, 10)
+#define MT_IFS_SIFS GENMASK(22, 16)
+#define MT_IFS_SLOT GENMASK(30, 24)
+
+#define MT_TMAC_ICR1(_band) MT_WF_TMAC(_band, 0x018)
+#define MT_IFS_EIFS_CCK GENMASK(8, 0)
+
+/* WF DMA TOP: band 0(0x820e7000), band 1(0x820f7000), band 2(0x830e7000) */
+#define MT_WF_DMA_BASE(_band) __BASE(WF_DMA_BASE, (_band))
+#define MT_WF_DMA(_band, ofs) (MT_WF_DMA_BASE(_band) + (ofs))
+
+#define MT_DMA_DCR0(_band) MT_WF_DMA(_band, 0x000)
+#define MT_DMA_DCR0_RXD_G5_EN BIT(23)
+
+#define MT_DMA_TCRF1(_band) MT_WF_DMA(_band, 0x054)
+#define MT_DMA_TCRF1_QIDX GENMASK(15, 13)
+
+/* WTBLOFF TOP: band 0(0x820e9000), band 1(0x820f9000), band 2(0x830e9000) */
+#define MT_WTBLOFF_BASE(_band) __BASE(WF_WTBLOFF_BASE, (_band))
+#define MT_WTBLOFF(_band, ofs) (MT_WTBLOFF_BASE(_band) + (ofs))
+
+#define MT_WTBLOFF_RSCR(_band) MT_WTBLOFF(_band, 0x008)
+#define MT_WTBLOFF_RSCR_RCPI_MODE GENMASK(31, 30)
+#define MT_WTBLOFF_RSCR_RCPI_PARAM GENMASK(25, 24)
+
+/* ETBF: band 0(0x820ea000), band 1(0x820fa000), band 2(0x830ea000) */
+#define MT_WF_ETBF_BASE(_band) __BASE(WF_ETBF_BASE, (_band))
+#define MT_WF_ETBF(_band, ofs) (MT_WF_ETBF_BASE(_band) + (ofs))
+
+#define MT_ETBF_RX_FB_CONT(_band) MT_WF_ETBF(_band, 0x100)
+#define MT_ETBF_RX_FB_BW GENMASK(10, 8)
+#define MT_ETBF_RX_FB_NC GENMASK(7, 4)
+#define MT_ETBF_RX_FB_NR GENMASK(3, 0)
+
+/* LPON: band 0(0x820eb000), band 1(0x820fb000), band 2(0x830eb000) */
+#define MT_WF_LPON_BASE(_band) __BASE(WF_LPON_BASE, (_band))
+#define MT_WF_LPON(_band, ofs) (MT_WF_LPON_BASE(_band) + (ofs))
+
+#define MT_LPON_UTTR0(_band) MT_WF_LPON(_band, 0x360)
+#define MT_LPON_UTTR1(_band) MT_WF_LPON(_band, 0x364)
+#define MT_LPON_FRCR(_band) MT_WF_LPON(_band, 0x37c)
+
+#define MT_LPON_TCR(_band, n) MT_WF_LPON(_band, 0x0a8 + (((n) * 4) << 4))
+#define MT_LPON_TCR_SW_MODE GENMASK(1, 0)
+#define MT_LPON_TCR_SW_WRITE BIT(0)
+#define MT_LPON_TCR_SW_ADJUST BIT(1)
+#define MT_LPON_TCR_SW_READ GENMASK(1, 0)
+
+/* MIB: band 0(0x820ed000), band 1(0x820fd000), band 2(0x830ed000)*/
+/* These counters are (mostly?) clear-on-read. So, some should not
+ * be read at all in case firmware is already reading them. These
+ * are commented with 'DNR' below. The DNR stats will be read by querying
+ * the firmware API for the appropriate message. For counters the driver
+ * does read, the driver should accumulate the counters.
+ */
+#define MT_WF_MIB_BASE(_band) __BASE(WF_MIB_BASE, (_band))
+#define MT_WF_MIB(_band, ofs) (MT_WF_MIB_BASE(_band) + (ofs))
+
+#define MT_MIB_BSCR0(_band) MT_WF_MIB(_band, 0x9cc)
+#define MT_MIB_BSCR1(_band) MT_WF_MIB(_band, 0x9d0)
+#define MT_MIB_BSCR2(_band) MT_WF_MIB(_band, 0x9d4)
+#define MT_MIB_BSCR3(_band) MT_WF_MIB(_band, 0x9d8)
+#define MT_MIB_BSCR4(_band) MT_WF_MIB(_band, 0x9dc)
+#define MT_MIB_BSCR5(_band) MT_WF_MIB(_band, 0x9e0)
+#define MT_MIB_BSCR6(_band) MT_WF_MIB(_band, 0x9e4)
+#define MT_MIB_BSCR7(_band) MT_WF_MIB(_band, 0x9e8)
+#define MT_MIB_BSCR17(_band) MT_WF_MIB(_band, 0xa10)
+
+#define MT_MIB_TSCR5(_band) MT_WF_MIB(_band, 0x6c4)
+#define MT_MIB_TSCR6(_band) MT_WF_MIB(_band, 0x6c8)
+#define MT_MIB_TSCR7(_band) MT_WF_MIB(_band, 0x6d0)
+
+#define MT_MIB_RSCR1(_band) MT_WF_MIB(_band, 0x7ac)
+/* rx mpdu counter, full 32 bits */
+#define MT_MIB_RSCR31(_band) MT_WF_MIB(_band, 0x964)
+#define MT_MIB_RSCR33(_band) MT_WF_MIB(_band, 0x96c)
+
+#define MT_MIB_SDR6(_band) MT_WF_MIB(_band, 0x020)
+#define MT_MIB_SDR6_CHANNEL_IDL_CNT_MASK GENMASK(15, 0)
+
+#define MT_MIB_RVSR0(_band) MT_WF_MIB(_band, 0x720)
+
+#define MT_MIB_RSCR35(_band) MT_WF_MIB(_band, 0x974)
+#define MT_MIB_RSCR36(_band) MT_WF_MIB(_band, 0x978)
+
+/* tx ampdu cnt, full 32 bits */
+#define MT_MIB_TSCR0(_band) MT_WF_MIB(_band, 0x6b0)
+#define MT_MIB_TSCR2(_band) MT_WF_MIB(_band, 0x6b8)
+
+/* counts all mpdus in ampdu, regardless of success */
+#define MT_MIB_TSCR3(_band) MT_WF_MIB(_band, 0x6bc)
+
+/* counts all successfully tx'd mpdus in ampdu */
+#define MT_MIB_TSCR4(_band) MT_WF_MIB(_band, 0x6c0)
+
+/* rx ampdu count, 32-bit */
+#define MT_MIB_RSCR27(_band) MT_WF_MIB(_band, 0x954)
+
+/* rx ampdu bytes count, 32-bit */
+#define MT_MIB_RSCR28(_band) MT_WF_MIB(_band, 0x958)
+
+/* rx ampdu valid subframe count */
+#define MT_MIB_RSCR29(_band) MT_WF_MIB(_band, 0x95c)
+
+/* rx ampdu valid subframe bytes count, 32bits */
+#define MT_MIB_RSCR30(_band) MT_WF_MIB(_band, 0x960)
+
+/* remaining windows protected stats */
+#define MT_MIB_SDR27(_band) MT_WF_MIB(_band, 0x080)
+#define MT_MIB_SDR27_TX_RWP_FAIL_CNT GENMASK(15, 0)
+
+#define MT_MIB_SDR28(_band) MT_WF_MIB(_band, 0x084)
+#define MT_MIB_SDR28_TX_RWP_NEED_CNT GENMASK(15, 0)
+
+#define MT_MIB_RVSR1(_band) MT_WF_MIB(_band, 0x724)
+
+/* rx blockack count, 32 bits */
+#define MT_MIB_TSCR1(_band) MT_WF_MIB(_band, 0x6b4)
+
+#define MT_MIB_BTSCR0(_band) MT_WF_MIB(_band, 0x5e0)
+#define MT_MIB_BTSCR5(_band) MT_WF_MIB(_band, 0x788)
+#define MT_MIB_BTSCR6(_band) MT_WF_MIB(_band, 0x798)
+
+#define MT_MIB_BFTFCR(_band) MT_WF_MIB(_band, 0x5d0)
+
+#define MT_TX_AGG_CNT(_band, n) MT_WF_MIB(_band, 0xa28 + ((n) << 2))
+#define MT_MIB_ARNG(_band, n) MT_WF_MIB(_band, 0x0b0 + ((n) << 2))
+#define MT_MIB_ARNCR_RANGE(val, n) (((val) >> ((n) << 4)) & GENMASK(9, 0))
+
+/* UMIB */
+#define MT_WF_UMIB_BASE 0x820cd000
+#define MT_WF_UMIB(ofs) (MT_WF_UMIB_BASE + (ofs))
+
+#define MT_UMIB_RPDCR(_band) (MT_WF_UMIB(0x594) + (_band) * 0x164)
+
+/* 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_GROUP GENMASK(4, 0)
+
+#define MT_WTBL_UPDATE MT_WTBLON_TOP(0x380)
+#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)
+
+/* WTBL */
+#define MT_WTBL_BASE 0x820d8000
+#define MT_WTBL_LMAC_ID GENMASK(14, 8)
+#define MT_WTBL_LMAC_DW GENMASK(7, 2)
+#define MT_WTBL_LMAC_OFFS(_id, _dw) (MT_WTBL_BASE | \
+ FIELD_PREP(MT_WTBL_LMAC_ID, _id) | \
+ FIELD_PREP(MT_WTBL_LMAC_DW, _dw))
+
+/* AGG: band 0(0x820e2000), band 1(0x820f2000), band 2(0x830e2000) */
+#define MT_WF_AGG_BASE(_band) __BASE(WF_AGG_BASE, (_band))
+#define MT_WF_AGG(_band, ofs) (MT_WF_AGG_BASE(_band) + (ofs))
+
+#define MT_AGG_ACR0(_band) MT_WF_AGG(_band, 0x054)
+#define MT_AGG_ACR_CFEND_RATE GENMASK(13, 0)
+
+/* ARB: band 0(0x820e3000), band 1(0x820f3000), band 2(0x830e3000) */
+#define MT_WF_ARB_BASE(_band) __BASE(WF_ARB_BASE, (_band))
+#define MT_WF_ARB(_band, ofs) (MT_WF_ARB_BASE(_band) + (ofs))
+
+#define MT_ARB_SCR(_band) MT_WF_ARB(_band, 0x000)
+#define MT_ARB_SCR_TX_DISABLE BIT(8)
+#define MT_ARB_SCR_RX_DISABLE BIT(9)
+
+/* RMAC: band 0(0x820e5000), band 1(0x820f5000), band 2(0x830e5000), */
+#define MT_WF_RMAC_BASE(_band) __BASE(WF_RMAC_BASE, (_band))
+#define MT_WF_RMAC(_band, ofs) (MT_WF_RMAC_BASE(_band) + (ofs))
+
+#define MT_WF_RFCR(_band) MT_WF_RMAC(_band, 0x000)
+#define MT_WF_RFCR_DROP_STBC_MULTI BIT(0)
+#define MT_WF_RFCR_DROP_FCSFAIL BIT(1)
+#define MT_WF_RFCR_DROP_PROBEREQ BIT(4)
+#define MT_WF_RFCR_DROP_MCAST BIT(5)
+#define MT_WF_RFCR_DROP_BCAST BIT(6)
+#define MT_WF_RFCR_DROP_MCAST_FILTERED BIT(7)
+#define MT_WF_RFCR_DROP_A3_MAC BIT(8)
+#define MT_WF_RFCR_DROP_A3_BSSID BIT(9)
+#define MT_WF_RFCR_DROP_A2_BSSID BIT(10)
+#define MT_WF_RFCR_DROP_OTHER_BEACON BIT(11)
+#define MT_WF_RFCR_DROP_FRAME_REPORT BIT(12)
+#define MT_WF_RFCR_DROP_CTL_RSV BIT(13)
+#define MT_WF_RFCR_DROP_CTS BIT(14)
+#define MT_WF_RFCR_DROP_RTS BIT(15)
+#define MT_WF_RFCR_DROP_DUPLICATE BIT(16)
+#define MT_WF_RFCR_DROP_OTHER_BSS BIT(17)
+#define MT_WF_RFCR_DROP_OTHER_UC BIT(18)
+#define MT_WF_RFCR_DROP_OTHER_TIM BIT(19)
+#define MT_WF_RFCR_DROP_NDPA BIT(20)
+#define MT_WF_RFCR_DROP_UNWANTED_CTL BIT(21)
+
+#define MT_WF_RFCR1(_band) MT_WF_RMAC(_band, 0x004)
+#define MT_WF_RFCR1_DROP_ACK BIT(4)
+#define MT_WF_RFCR1_DROP_BF_POLL BIT(5)
+#define MT_WF_RFCR1_DROP_BA BIT(6)
+#define MT_WF_RFCR1_DROP_CFEND BIT(7)
+#define MT_WF_RFCR1_DROP_CFACK BIT(8)
+
+#define MT_WF_RMAC_MIB_AIRTIME0(_band) MT_WF_RMAC(_band, 0x0380)
+#define MT_WF_RMAC_MIB_RXTIME_CLR BIT(31)
+#define MT_WF_RMAC_MIB_ED_OFFSET GENMASK(20, 16)
+#define MT_WF_RMAC_MIB_OBSS_BACKOFF GENMASK(15, 0)
+
+#define MT_WF_RMAC_MIB_AIRTIME1(_band) MT_WF_RMAC(_band, 0x0384)
+#define MT_WF_RMAC_MIB_NONQOSD_BACKOFF GENMASK(31, 16)
+
+#define MT_WF_RMAC_MIB_AIRTIME3(_band) MT_WF_RMAC(_band, 0x038c)
+#define MT_WF_RMAC_MIB_QOS01_BACKOFF GENMASK(31, 0)
+
+#define MT_WF_RMAC_MIB_AIRTIME4(_band) MT_WF_RMAC(_band, 0x0390)
+#define MT_WF_RMAC_MIB_QOS23_BACKOFF GENMASK(31, 0)
+
+#define MT_WF_RMAC_RSVD0(_band) MT_WF_RMAC(_band, 0x03e0)
+#define MT_WF_RMAC_RSVD0_EIFS_CLR BIT(21)
+
+/* WFDMA0 */
+#define MT_WFDMA0_BASE 0xd4000
+#define MT_WFDMA0(ofs) (MT_WFDMA0_BASE + (ofs))
+
+#define MT_WFDMA0_RST MT_WFDMA0(0x100)
+#define MT_WFDMA0_RST_LOGIC_RST BIT(4)
+#define MT_WFDMA0_RST_DMASHDL_ALL_RST BIT(5)
+
+#define MT_WFDMA0_BUSY_ENA MT_WFDMA0(0x13c)
+#define MT_WFDMA0_BUSY_ENA_TX_FIFO0 BIT(0)
+#define MT_WFDMA0_BUSY_ENA_TX_FIFO1 BIT(1)
+#define MT_WFDMA0_BUSY_ENA_RX_FIFO BIT(2)
+
+#define MT_WFDMA0_RX_INT_PCIE_SEL MT_WFDMA0(0x154)
+#define MT_WFDMA0_RX_INT_SEL_RING3 BIT(3)
+
+#define MT_WFDMA0_GLO_CFG MT_WFDMA0(0x208)
+#define MT_WFDMA0_GLO_CFG_TX_DMA_EN BIT(0)
+#define MT_WFDMA0_GLO_CFG_RX_DMA_EN BIT(2)
+#define MT_WFDMA0_GLO_CFG_OMIT_TX_INFO BIT(28)
+#define MT_WFDMA0_GLO_CFG_OMIT_RX_INFO BIT(27)
+#define MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2 BIT(21)
+
+#define WF_WFDMA0_GLO_CFG_EXT0 MT_WFDMA0(0x2b0)
+#define WF_WFDMA0_GLO_CFG_EXT0_RX_WB_RXD BIT(18)
+#define WF_WFDMA0_GLO_CFG_EXT0_WED_MERGE_MODE BIT(14)
+
+#define WF_WFDMA0_GLO_CFG_EXT1 MT_WFDMA0(0x2b4)
+#define WF_WFDMA0_GLO_CFG_EXT1_CALC_MODE BIT(31)
+#define WF_WFDMA0_GLO_CFG_EXT1_TX_FCTRL_MODE BIT(28)
+
+#define MT_WFDMA0_RST_DTX_PTR MT_WFDMA0(0x20c)
+#define MT_WFDMA0_PRI_DLY_INT_CFG0 MT_WFDMA0(0x2f0)
+#define MT_WFDMA0_PRI_DLY_INT_CFG1 MT_WFDMA0(0x2f4)
+#define MT_WFDMA0_PRI_DLY_INT_CFG2 MT_WFDMA0(0x2f8)
+
+/* WFDMA1 */
+#define MT_WFDMA1_BASE 0xd5000
+
+/* WFDMA CSR */
+#define MT_WFDMA_EXT_CSR_BASE 0xd7000
+#define MT_WFDMA_EXT_CSR(ofs) (MT_WFDMA_EXT_CSR_BASE + (ofs))
+
+#define MT_WFDMA_HOST_CONFIG MT_WFDMA_EXT_CSR(0x30)
+#define MT_WFDMA_HOST_CONFIG_PDMA_BAND BIT(0)
+
+#define MT_WFDMA_EXT_CSR_HIF_MISC MT_WFDMA_EXT_CSR(0x44)
+#define MT_WFDMA_EXT_CSR_HIF_MISC_BUSY BIT(0)
+
+#define MT_PCIE_RECOG_ID 0xd7090
+#define MT_PCIE_RECOG_ID_MASK GENMASK(30, 0)
+#define MT_PCIE_RECOG_ID_SEM BIT(31)
+
+/* WFDMA0 PCIE1 */
+#define MT_WFDMA0_PCIE1_BASE 0xd8000
+#define MT_WFDMA0_PCIE1(ofs) (MT_WFDMA0_PCIE1_BASE + (ofs))
+
+#define MT_WFDMA0_PCIE1_BUSY_ENA MT_WFDMA0_PCIE1(0x13c)
+#define MT_WFDMA0_PCIE1_BUSY_ENA_TX_FIFO0 BIT(0)
+#define MT_WFDMA0_PCIE1_BUSY_ENA_TX_FIFO1 BIT(1)
+#define MT_WFDMA0_PCIE1_BUSY_ENA_RX_FIFO BIT(2)
+
+/* WFDMA COMMON */
+#define __RXQ(q) ((q) + __MT_MCUQ_MAX)
+#define __TXQ(q) (__RXQ(q) + __MT_RXQ_MAX)
+
+#define MT_Q_ID(q) (dev->q_id[(q)])
+#define MT_Q_BASE(q) ((dev->q_wfdma_mask >> (q)) & 0x1 ? \
+ MT_WFDMA1_BASE : MT_WFDMA0_BASE)
+
+#define MT_MCUQ_ID(q) MT_Q_ID(q)
+#define MT_TXQ_ID(q) MT_Q_ID(__TXQ(q))
+#define MT_RXQ_ID(q) MT_Q_ID(__RXQ(q))
+
+#define MT_MCUQ_RING_BASE(q) (MT_Q_BASE(q) + 0x300)
+#define MT_TXQ_RING_BASE(q) (MT_Q_BASE(__TXQ(q)) + 0x300)
+#define MT_RXQ_RING_BASE(q) (MT_Q_BASE(__RXQ(q)) + 0x500)
+
+#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 + \
+ MT_RXQ_ID(q) * 0x4)
+#define MT_TXQ_EXT_CTRL(q) (MT_Q_BASE(__TXQ(q)) + 0x600 + \
+ MT_TXQ_ID(q) * 0x4)
+
+#define MT_INT_SOURCE_CSR MT_WFDMA0(0x200)
+#define MT_INT_MASK_CSR MT_WFDMA0(0x204)
+
+#define MT_INT1_SOURCE_CSR MT_WFDMA0_PCIE1(0x200)
+#define MT_INT1_MASK_CSR MT_WFDMA0_PCIE1(0x204)
+
+#define MT_INT_RX_DONE_BAND0 BIT(12)
+#define MT_INT_RX_DONE_BAND1 BIT(12)
+#define MT_INT_RX_DONE_BAND2 BIT(13)
+#define MT_INT_RX_DONE_WM BIT(0)
+#define MT_INT_RX_DONE_WA BIT(1)
+#define MT_INT_RX_DONE_WA_MAIN BIT(2)
+#define MT_INT_RX_DONE_WA_EXT BIT(2)
+#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_MCU_CMD BIT(29)
+
+#define MT_INT_RX(q) (dev->q_int_mask[__RXQ(q)])
+#define MT_INT_TX_MCU(q) (dev->q_int_mask[(q)])
+
+#define MT_INT_RX_DONE_MCU (MT_INT_RX(MT_RXQ_MCU) | \
+ MT_INT_RX(MT_RXQ_MCU_WA))
+
+#define MT_INT_BAND0_RX_DONE (MT_INT_RX(MT_RXQ_MAIN) | \
+ MT_INT_RX(MT_RXQ_MAIN_WA))
+
+#define MT_INT_BAND1_RX_DONE (MT_INT_RX(MT_RXQ_BAND1) | \
+ MT_INT_RX(MT_RXQ_BAND1_WA) | \
+ MT_INT_RX(MT_RXQ_MAIN_WA))
+
+#define MT_INT_BAND2_RX_DONE (MT_INT_RX(MT_RXQ_BAND2) | \
+ MT_INT_RX(MT_RXQ_BAND2_WA) | \
+ MT_INT_RX(MT_RXQ_MAIN_WA))
+
+#define MT_INT_RX_DONE_ALL (MT_INT_RX_DONE_MCU | \
+ MT_INT_BAND0_RX_DONE | \
+ MT_INT_BAND1_RX_DONE | \
+ MT_INT_BAND2_RX_DONE)
+
+#define MT_INT_TX_DONE_FWDL BIT(26)
+#define MT_INT_TX_DONE_MCU_WM BIT(27)
+#define MT_INT_TX_DONE_MCU_WA BIT(22)
+#define MT_INT_TX_DONE_BAND0 BIT(30)
+#define MT_INT_TX_DONE_BAND1 BIT(31)
+#define MT_INT_TX_DONE_BAND2 BIT(15)
+
+#define MT_INT_TX_DONE_MCU (MT_INT_TX_MCU(MT_MCUQ_WA) | \
+ MT_INT_TX_MCU(MT_MCUQ_WM) | \
+ MT_INT_TX_MCU(MT_MCUQ_FWDL))
+
+#define MT_MCU_CMD MT_WFDMA0(0x1f0)
+#define MT_MCU_CMD_STOP_DMA BIT(2)
+#define MT_MCU_CMD_RESET_DONE BIT(3)
+#define MT_MCU_CMD_RECOVERY_DONE BIT(4)
+#define MT_MCU_CMD_NORMAL_STATE BIT(5)
+#define MT_MCU_CMD_ERROR_MASK GENMASK(5, 1)
+
+/* l1/l2 remap */
+#define MT_HIF_REMAP_L1 0x155024
+#define MT_HIF_REMAP_L1_MASK GENMASK(31, 16)
+#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_L2 0x1b4
+#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_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_CBTOP2_PHY_START 0xf0000000
+#define MT_CBTOP2_PHY_END 0xffffffff
+#define MT_INFRA_MCU_START 0x7c000000
+#define MT_INFRA_MCU_END 0x7c3fffff
+
+/* FW MODE SYNC */
+#define MT_SWDEF_MODE 0x9143c
+#define MT_SWDEF_NORMAL_MODE 0
+
+/* LED */
+#define MT_LED_TOP_BASE 0x18013000
+#define MT_LED_PHYS(_n) (MT_LED_TOP_BASE + (_n))
+
+#define MT_LED_CTRL(_n) MT_LED_PHYS(0x00 + ((_n) * 4))
+#define MT_LED_CTRL_KICK BIT(7)
+#define MT_LED_CTRL_BLINK_MODE BIT(2)
+#define MT_LED_CTRL_POLARITY BIT(1)
+
+#define MT_LED_TX_BLINK(_n) MT_LED_PHYS(0x10 + ((_n) * 4))
+#define MT_LED_TX_BLINK_ON_MASK GENMASK(7, 0)
+#define MT_LED_TX_BLINK_OFF_MASK GENMASK(15, 8)
+
+#define MT_LED_EN(_n) MT_LED_PHYS(0x40 + ((_n) * 4))
+
+#define MT_LED_GPIO_MUX2 0x70005058 /* GPIO 18 */
+#define MT_LED_GPIO_MUX3 0x7000505C /* GPIO 26 */
+#define MT_LED_GPIO_SEL_MASK GENMASK(11, 8)
+
+/* MT TOP */
+#define MT_TOP_BASE 0xe0000
+#define MT_TOP(ofs) (MT_TOP_BASE + (ofs))
+
+#define MT_TOP_LPCR_HOST_BAND(_band) MT_TOP(0x10 + ((_band) * 0x10))
+#define MT_TOP_LPCR_HOST_FW_OWN BIT(0)
+#define MT_TOP_LPCR_HOST_DRV_OWN BIT(1)
+#define MT_TOP_LPCR_HOST_FW_OWN_STAT BIT(2)
+
+#define MT_TOP_LPCR_HOST_BAND_IRQ_STAT(_band) MT_TOP(0x14 + ((_band) * 0x10))
+#define MT_TOP_LPCR_HOST_BAND_STAT BIT(0)
+
+#define MT_TOP_MISC MT_TOP(0xf0)
+#define MT_TOP_MISC_FW_STATE GENMASK(2, 0)
+
+#define MT_HW_REV 0x70010204
+#define MT_WF_SUBSYS_RST 0x70002600
+
+/* PCIE MAC */
+#define MT_PCIE_MAC_BASE 0x74030000
+#define MT_PCIE_MAC(ofs) (MT_PCIE_MAC_BASE + (ofs))
+#define MT_PCIE_MAC_INT_ENABLE MT_PCIE_MAC(0x188)
+
+#define MT_PCIE1_MAC_BASE 0x74090000
+#define MT_PCIE1_MAC(ofs) (MT_PCIE1_MAC_BASE + (ofs))
+
+#define MT_PCIE1_MAC_INT_ENABLE MT_PCIE1_MAC(0x188)
+
+/* PHYRX CTRL */
+#define MT_WF_PHYRX_BAND_BASE 0x83080000
+#define MT_WF_PHYRX_BAND(_band, ofs) (MT_WF_PHYRX_BAND_BASE + \
+ ((_band) << 20) + (ofs))
+
+#define MT_WF_PHYRX_BAND_RX_CTRL1(_band) MT_WF_PHYRX_BAND(_band, 0x2004)
+#define MT_WF_PHYRX_BAND_RX_CTRL1_IPI_EN GENMASK(2, 0)
+#define MT_WF_PHYRX_BAND_RX_CTRL1_STSCNT_EN GENMASK(11, 9)
+
+/* PHYRX CSD */
+#define MT_WF_PHYRX_CSD_BASE 0x83000000
+#define MT_WF_PHYRX_CSD(_band, _wf, ofs) (MT_WF_PHYRX_CSD_BASE + \
+ ((_band) << 20) + \
+ ((_wf) << 16) + (ofs))
+#define MT_WF_PHYRX_CSD_IRPI(_band, _wf) MT_WF_PHYRX_CSD(_band, _wf, 0x1000)
+
+/* PHYRX CSD BAND */
+#define MT_WF_PHYRX_CSD_BAND_RXTD12(_band) MT_WF_PHYRX_BAND(_band, 0x8230)
+#define MT_WF_PHYRX_CSD_BAND_RXTD12_IRPI_SW_CLR_ONLY BIT(18)
+#define MT_WF_PHYRX_CSD_BAND_RXTD12_IRPI_SW_CLR BIT(29)
+
+#endif
diff --git a/drivers/net/wireless/mediatek/mt76/sdio.c b/drivers/net/wireless/mediatek/mt76/sdio.c
index 0ec308f99af5..228bc7d45011 100644
--- a/drivers/net/wireless/mediatek/mt76/sdio.c
+++ b/drivers/net/wireless/mediatek/mt76/sdio.c
@@ -395,7 +395,7 @@ mt76s_process_rx_queue(struct mt76_dev *dev, struct mt76_queue *q)
if (!e || !e->skb)
break;
- dev->drv->rx_skb(dev, MT_RXQ_MAIN, e->skb);
+ dev->drv->rx_skb(dev, MT_RXQ_MAIN, e->skb, NULL);
e->skb = NULL;
nframes++;
}
diff --git a/drivers/net/wireless/mediatek/mt76/tx.c b/drivers/net/wireless/mediatek/mt76/tx.c
index 6c054850363f..24568b98ed9d 100644
--- a/drivers/net/wireless/mediatek/mt76/tx.c
+++ b/drivers/net/wireless/mediatek/mt76/tx.c
@@ -756,6 +756,23 @@ int mt76_token_consume(struct mt76_dev *dev, struct mt76_txwi_cache **ptxwi)
}
EXPORT_SYMBOL_GPL(mt76_token_consume);
+int mt76_rx_token_consume(struct mt76_dev *dev, void *ptr,
+ struct mt76_txwi_cache *t, dma_addr_t phys)
+{
+ int token;
+
+ spin_lock_bh(&dev->rx_token_lock);
+ token = idr_alloc(&dev->rx_token, t, 0, dev->rx_token_size,
+ GFP_ATOMIC);
+ spin_unlock_bh(&dev->rx_token_lock);
+
+ t->ptr = ptr;
+ t->dma_addr = phys;
+
+ return token;
+}
+EXPORT_SYMBOL_GPL(mt76_rx_token_consume);
+
struct mt76_txwi_cache *
mt76_token_release(struct mt76_dev *dev, int token, bool *wake)
{
@@ -784,3 +801,16 @@ mt76_token_release(struct mt76_dev *dev, int token, bool *wake)
return txwi;
}
EXPORT_SYMBOL_GPL(mt76_token_release);
+
+struct mt76_txwi_cache *
+mt76_rx_token_release(struct mt76_dev *dev, int token)
+{
+ struct mt76_txwi_cache *t;
+
+ spin_lock_bh(&dev->rx_token_lock);
+ t = idr_remove(&dev->rx_token, token);
+ spin_unlock_bh(&dev->rx_token_lock);
+
+ return t;
+}
+EXPORT_SYMBOL_GPL(mt76_rx_token_release);
diff --git a/drivers/net/wireless/mediatek/mt76/usb.c b/drivers/net/wireless/mediatek/mt76/usb.c
index 4c4033bb1bb3..3e281715fcd4 100644
--- a/drivers/net/wireless/mediatek/mt76/usb.c
+++ b/drivers/net/wireless/mediatek/mt76/usb.c
@@ -547,7 +547,7 @@ mt76u_process_rx_entry(struct mt76_dev *dev, struct urb *urb,
len -= data_len;
nsgs++;
}
- dev->drv->rx_skb(dev, MT_RXQ_MAIN, skb);
+ dev->drv->rx_skb(dev, MT_RXQ_MAIN, skb, NULL);
return nsgs;
}
@@ -766,6 +766,9 @@ static void mt76u_status_worker(struct mt76_worker *w)
struct mt76_queue *q;
int i;
+ if (!test_bit(MT76_STATE_RUNNING, &dev->phy.state))
+ return;
+
for (i = 0; i < IEEE80211_NUM_ACS; i++) {
q = dev->phy.q_tx[i];
if (!q)
@@ -785,11 +788,11 @@ static void mt76u_status_worker(struct mt76_worker *w)
wake_up(&dev->tx_wait);
mt76_worker_schedule(&dev->tx_worker);
-
- if (dev->drv->tx_status_data &&
- !test_and_set_bit(MT76_READING_STATS, &dev->phy.state))
- queue_work(dev->wq, &dev->usb.stat_work);
}
+
+ if (dev->drv->tx_status_data &&
+ !test_and_set_bit(MT76_READING_STATS, &dev->phy.state))
+ queue_work(dev->wq, &dev->usb.stat_work);
}
static void mt76u_tx_status_data(struct work_struct *work)
diff --git a/drivers/net/wireless/mediatek/mt76/util.h b/drivers/net/wireless/mediatek/mt76/util.h
index 49c52d781f40..260965dde94c 100644
--- a/drivers/net/wireless/mediatek/mt76/util.h
+++ b/drivers/net/wireless/mediatek/mt76/util.h
@@ -29,12 +29,6 @@ enum {
int mt76_wcid_alloc(u32 *mask, int size);
-static inline bool
-mt76_wcid_mask_test(u32 *mask, int idx)
-{
- return mask[idx / 32] & BIT(idx % 32);
-}
-
static inline void
mt76_wcid_mask_set(u32 *mask, int idx)
{
diff --git a/drivers/net/wireless/purelifi/plfxlc/usb.c b/drivers/net/wireless/purelifi/plfxlc/usb.c
index 39e54b3787d6..76d0a778636a 100644
--- a/drivers/net/wireless/purelifi/plfxlc/usb.c
+++ b/drivers/net/wireless/purelifi/plfxlc/usb.c
@@ -247,6 +247,7 @@ error:
for (i = 0; i < RX_URBS_COUNT; i++)
free_rx_urb(urbs[i]);
}
+ kfree(urbs);
return r;
}
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
index e8fcd531c437..28f136064297 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
+++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
@@ -4645,7 +4645,6 @@ rtl8xxxu_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
if (sta->deflink.ht_cap.cap &
(IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_SGI_20))
sgi = 1;
- rcu_read_unlock();
highest_rate = fls(ramask) - 1;
if (highest_rate < DESC_RATE_MCS0) {
@@ -4670,6 +4669,7 @@ rtl8xxxu_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
else
rarpt->txrate.bw = RATE_INFO_BW_20;
}
+ rcu_read_unlock();
bit_rate = cfg80211_calculate_bitrate(&rarpt->txrate);
rarpt->bit_rate = bit_rate;
rarpt->desc_rate = highest_rate;
diff --git a/drivers/net/wireless/realtek/rtw88/fw.c b/drivers/net/wireless/realtek/rtw88/fw.c
index 0b5f903c0f36..5e760c884f89 100644
--- a/drivers/net/wireless/realtek/rtw88/fw.c
+++ b/drivers/net/wireless/realtek/rtw88/fw.c
@@ -311,10 +311,10 @@ EXPORT_SYMBOL(rtw_fw_c2h_cmd_isr);
static void rtw_fw_send_h2c_command(struct rtw_dev *rtwdev,
u8 *h2c)
{
+ struct rtw_h2c_cmd *h2c_cmd = (struct rtw_h2c_cmd *)h2c;
u8 box;
u8 box_state;
u32 box_reg, box_ex_reg;
- int idx;
int ret;
rtw_dbg(rtwdev, RTW_DBG_FW,
@@ -356,10 +356,8 @@ static void rtw_fw_send_h2c_command(struct rtw_dev *rtwdev,
goto out;
}
- for (idx = 0; idx < 4; idx++)
- rtw_write8(rtwdev, box_reg + idx, h2c[idx]);
- for (idx = 0; idx < 4; idx++)
- rtw_write8(rtwdev, box_ex_reg + idx, h2c[idx + 4]);
+ rtw_write32(rtwdev, box_ex_reg, le32_to_cpu(h2c_cmd->msg_ext));
+ rtw_write32(rtwdev, box_reg, le32_to_cpu(h2c_cmd->msg));
if (++rtwdev->h2c.last_box_num >= 4)
rtwdev->h2c.last_box_num = 0;
@@ -824,6 +822,16 @@ void rtw_fw_set_nlo_info(struct rtw_dev *rtwdev, bool enable)
rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
}
+void rtw_fw_set_recover_bt_device(struct rtw_dev *rtwdev)
+{
+ u8 h2c_pkt[H2C_PKT_SIZE] = {0};
+
+ SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_RECOVER_BT_DEV);
+ SET_RECOVER_BT_DEV_EN(h2c_pkt, 1);
+
+ rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
+}
+
void rtw_fw_set_pg_info(struct rtw_dev *rtwdev)
{
struct rtw_lps_conf *conf = &rtwdev->lps_conf;
diff --git a/drivers/net/wireless/realtek/rtw88/fw.h b/drivers/net/wireless/realtek/rtw88/fw.h
index a5a965803a3c..0a386e6d6e0d 100644
--- a/drivers/net/wireless/realtek/rtw88/fw.h
+++ b/drivers/net/wireless/realtek/rtw88/fw.h
@@ -81,6 +81,11 @@ struct rtw_c2h_adaptivity {
u8 option;
} __packed;
+struct rtw_h2c_cmd {
+ __le32 msg;
+ __le32 msg_ext;
+} __packed;
+
enum rtw_rsvd_packet_type {
RSVD_BEACON,
RSVD_DUMMY,
@@ -550,6 +555,8 @@ static inline void rtw_h2c_pkt_set_header(u8 *h2c_pkt, u8 sub_id)
#define H2C_CMD_AOAC_GLOBAL_INFO 0x82
#define H2C_CMD_NLO_INFO 0x8C
+#define H2C_CMD_RECOVER_BT_DEV 0xD1
+
#define SET_H2C_CMD_ID_CLASS(h2c_pkt, value) \
le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(7, 0))
@@ -749,6 +756,9 @@ static inline void rtw_h2c_pkt_set_header(u8 *h2c_pkt, u8 sub_id)
#define SET_NLO_LOC_NLO_INFO(h2c_pkt, value) \
le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, GENMASK(23, 16))
+#define SET_RECOVER_BT_DEV_EN(h2c_pkt, value) \
+ le32p_replace_bits((__le32 *)(h2c_pkt) + 0x00, value, BIT(8))
+
#define GET_FW_DUMP_LEN(_header) \
le32_get_bits(*((__le32 *)(_header) + 0x00), GENMASK(15, 0))
#define GET_FW_DUMP_SEQ(_header) \
@@ -838,6 +848,7 @@ void rtw_fw_set_aoac_global_info_cmd(struct rtw_dev *rtwdev,
u8 group_key_enc);
void rtw_fw_set_nlo_info(struct rtw_dev *rtwdev, bool enable);
+void rtw_fw_set_recover_bt_device(struct rtw_dev *rtwdev);
void rtw_fw_update_pkt_probe_req(struct rtw_dev *rtwdev,
struct cfg80211_ssid *ssid);
void rtw_fw_channel_switch(struct rtw_dev *rtwdev, bool enable);
diff --git a/drivers/net/wireless/realtek/rtw88/mac.c b/drivers/net/wireless/realtek/rtw88/mac.c
index 52076e89d59a..c7e64f7036ac 100644
--- a/drivers/net/wireless/realtek/rtw88/mac.c
+++ b/drivers/net/wireless/realtek/rtw88/mac.c
@@ -906,7 +906,8 @@ out:
return ret;
}
-int rtw_download_firmware(struct rtw_dev *rtwdev, struct rtw_fw_state *fw)
+static
+int _rtw_download_firmware(struct rtw_dev *rtwdev, struct rtw_fw_state *fw)
{
if (rtw_chip_wcpu_11n(rtwdev))
return __rtw_download_firmware_legacy(rtwdev, fw);
@@ -914,6 +915,21 @@ int rtw_download_firmware(struct rtw_dev *rtwdev, struct rtw_fw_state *fw)
return __rtw_download_firmware(rtwdev, fw);
}
+int rtw_download_firmware(struct rtw_dev *rtwdev, struct rtw_fw_state *fw)
+{
+ int ret;
+
+ ret = _rtw_download_firmware(rtwdev, fw);
+ if (ret)
+ return ret;
+
+ if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_PCIE &&
+ rtwdev->chip->id == RTW_CHIP_TYPE_8821C)
+ rtw_fw_set_recover_bt_device(rtwdev);
+
+ return 0;
+}
+
static u32 get_priority_queues(struct rtw_dev *rtwdev, u32 queues)
{
const struct rtw_rqpn *rqpn = rtwdev->fifo.rqpn;
diff --git a/drivers/net/wireless/realtek/rtw89/chan.c b/drivers/net/wireless/realtek/rtw89/chan.c
index a4f61c2f6512..90596806bc93 100644
--- a/drivers/net/wireless/realtek/rtw89/chan.c
+++ b/drivers/net/wireless/realtek/rtw89/chan.c
@@ -4,6 +4,7 @@
#include "chan.h"
#include "debug.h"
+#include "util.h"
static enum rtw89_subband rtw89_get_subband_type(enum rtw89_band band,
u8 center_chan)
@@ -108,8 +109,8 @@ bool rtw89_assign_entity_chan(struct rtw89_dev *rtwdev,
const struct rtw89_chan *new)
{
struct rtw89_hal *hal = &rtwdev->hal;
- struct rtw89_chan *chan = &hal->chan[idx];
- struct rtw89_chan_rcd *rcd = &hal->chan_rcd[idx];
+ struct rtw89_chan *chan = &hal->sub[idx].chan;
+ struct rtw89_chan_rcd *rcd = &hal->sub[idx].rcd;
bool band_changed;
rcd->prev_primary_channel = chan->primary_channel;
@@ -127,7 +128,7 @@ static void __rtw89_config_entity_chandef(struct rtw89_dev *rtwdev,
{
struct rtw89_hal *hal = &rtwdev->hal;
- hal->chandef[idx] = *chandef;
+ hal->sub[idx].chandef = *chandef;
if (from_stack)
set_bit(idx, hal->entity_map);
@@ -195,6 +196,7 @@ int rtw89_chanctx_ops_add(struct rtw89_dev *rtwdev,
rtw89_config_entity_chandef(rtwdev, idx, &ctx->def);
rtw89_set_channel(rtwdev);
cfg->idx = idx;
+ hal->sub[idx].cfg = cfg;
return 0;
}
@@ -203,8 +205,34 @@ void rtw89_chanctx_ops_remove(struct rtw89_dev *rtwdev,
{
struct rtw89_hal *hal = &rtwdev->hal;
struct rtw89_chanctx_cfg *cfg = (struct rtw89_chanctx_cfg *)ctx->drv_priv;
+ struct rtw89_vif *rtwvif;
+ u8 drop, roll;
- clear_bit(cfg->idx, hal->entity_map);
+ drop = cfg->idx;
+ if (drop != RTW89_SUB_ENTITY_0)
+ goto out;
+
+ roll = find_next_bit(hal->entity_map, NUM_OF_RTW89_SUB_ENTITY, drop + 1);
+
+ /* Follow rtw89_config_default_chandef() when rtw89_entity_recalc(). */
+ if (roll == NUM_OF_RTW89_SUB_ENTITY)
+ goto out;
+
+ /* RTW89_SUB_ENTITY_0 is going to release, and another exists.
+ * Make another roll down to RTW89_SUB_ENTITY_0 to replace.
+ */
+ hal->sub[roll].cfg->idx = RTW89_SUB_ENTITY_0;
+ hal->sub[RTW89_SUB_ENTITY_0] = hal->sub[roll];
+
+ rtw89_for_each_rtwvif(rtwdev, rtwvif) {
+ if (rtwvif->sub_entity_idx == roll)
+ rtwvif->sub_entity_idx = RTW89_SUB_ENTITY_0;
+ }
+
+ drop = roll;
+
+out:
+ clear_bit(drop, hal->entity_map);
rtw89_set_channel(rtwdev);
}
@@ -225,6 +253,9 @@ int rtw89_chanctx_ops_assign_vif(struct rtw89_dev *rtwdev,
struct rtw89_vif *rtwvif,
struct ieee80211_chanctx_conf *ctx)
{
+ struct rtw89_chanctx_cfg *cfg = (struct rtw89_chanctx_cfg *)ctx->drv_priv;
+
+ rtwvif->sub_entity_idx = cfg->idx;
return 0;
}
@@ -232,4 +263,5 @@ void rtw89_chanctx_ops_unassign_vif(struct rtw89_dev *rtwdev,
struct rtw89_vif *rtwvif,
struct ieee80211_chanctx_conf *ctx)
{
+ rtwvif->sub_entity_idx = RTW89_SUB_ENTITY_0;
}
diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c
index f30aadc41f2b..5ab95250755d 100644
--- a/drivers/net/wireless/realtek/rtw89/core.c
+++ b/drivers/net/wireless/realtek/rtw89/core.c
@@ -1196,7 +1196,11 @@ static void rtw89_core_parse_phy_status_ie01(struct rtw89_dev *rtwdev, u8 *addr,
if (phy_ppdu->rate < RTW89_HW_RATE_OFDM6)
return;
/* sign conversion for S(12,2) */
- cfo = sign_extend32(RTW89_GET_PHY_STS_IE01_CFO(addr), 11);
+ if (rtwdev->chip->cfo_src_fd)
+ cfo = sign_extend32(RTW89_GET_PHY_STS_IE01_FD_CFO(addr), 11);
+ else
+ cfo = sign_extend32(RTW89_GET_PHY_STS_IE01_PREMB_CFO(addr), 11);
+
rtw89_phy_cfo_parse(rtwdev, cfo, phy_ppdu);
}
@@ -1401,6 +1405,9 @@ static void rtw89_vif_rx_stats_iter(void *data, u8 *mac,
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
const u8 *bssid = iter_data->bssid;
+ if (!vif->bss_conf.bssid)
+ return;
+
if (ieee80211_is_trigger(hdr->frame_control)) {
rtw89_stats_trigger_frame(rtwdev, vif, skb);
return;
@@ -1473,6 +1480,27 @@ static void rtw89_core_hw_to_sband_rate(struct ieee80211_rx_status *rx_status)
rx_status->rate_idx -= 4;
}
+static void rtw89_core_update_radiotap(struct rtw89_dev *rtwdev,
+ struct sk_buff *skb,
+ struct ieee80211_rx_status *rx_status)
+{
+ static const struct ieee80211_radiotap_he known_he = {
+ .data1 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_DATA_MCS_KNOWN |
+ IEEE80211_RADIOTAP_HE_DATA1_BW_RU_ALLOC_KNOWN),
+ .data2 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_GI_KNOWN),
+ };
+ struct ieee80211_radiotap_he *he;
+
+ if (!(rtwdev->hw->conf.flags & IEEE80211_CONF_MONITOR))
+ return;
+
+ if (rx_status->encoding == RX_ENC_HE) {
+ rx_status->flag |= RX_FLAG_RADIOTAP_HE;
+ he = skb_push(skb, sizeof(*he));
+ *he = known_he;
+ }
+}
+
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,
@@ -1487,6 +1515,7 @@ static void rtw89_core_rx_to_mac80211(struct rtw89_dev *rtwdev,
rtw89_core_hw_to_sband_rate(rx_status);
rtw89_core_rx_stats(rtwdev, phy_ppdu, desc_info, skb_ppdu);
+ rtw89_core_update_radiotap(rtwdev, skb_ppdu, rx_status);
/* In low power mode, it does RX in thread context. */
local_bh_disable();
ieee80211_rx_napi(rtwdev->hw, NULL, skb_ppdu, napi);
@@ -2233,6 +2262,7 @@ static void rtw89_track_work(struct work_struct *work)
rtw89_phy_ra_update(rtwdev);
rtw89_phy_cfo_track(rtwdev);
rtw89_phy_tx_path_div_track(rtwdev);
+ rtw89_phy_ul_tb_ctrl_track(rtwdev);
if (rtwdev->lps_enabled && !rtwdev->btc.lps)
rtw89_enter_lps_track(rtwdev);
@@ -2381,6 +2411,8 @@ void rtw89_vif_type_mapping(struct ieee80211_vif *vif, bool assoc)
rtwvif->self_role = RTW89_SELF_ROLE_CLIENT;
rtwvif->addr_cam.sec_ent_mode = RTW89_ADDR_CAM_SEC_NORMAL;
break;
+ case NL80211_IFTYPE_MONITOR:
+ break;
default:
WARN_ON(1);
break;
@@ -2556,6 +2588,7 @@ int rtw89_core_sta_assoc(struct rtw89_dev *rtwdev,
rtw89_btc_ntfy_role_info(rtwdev, rtwvif, rtwsta,
BTC_ROLE_MSTS_STA_CONN_END);
rtw89_core_get_no_ul_ofdma_htc(rtwdev, &rtwsta->htc_template);
+ rtw89_phy_ul_tb_assoc(rtwdev, rtwvif);
}
return ret;
@@ -2941,6 +2974,41 @@ void rtw89_core_update_beacon_work(struct work_struct *work)
mutex_unlock(&rtwdev->mutex);
}
+int rtw89_wait_for_cond(struct rtw89_wait_info *wait, unsigned int cond)
+{
+ struct completion *cmpl = &wait->completion;
+ unsigned long timeout;
+ unsigned int cur;
+
+ cur = atomic_cmpxchg(&wait->cond, RTW89_WAIT_COND_IDLE, cond);
+ if (cur != RTW89_WAIT_COND_IDLE)
+ return -EBUSY;
+
+ timeout = wait_for_completion_timeout(cmpl, RTW89_WAIT_FOR_COND_TIMEOUT);
+ if (timeout == 0) {
+ atomic_set(&wait->cond, RTW89_WAIT_COND_IDLE);
+ return -ETIMEDOUT;
+ }
+
+ if (wait->data.err)
+ return -EFAULT;
+
+ return 0;
+}
+
+void rtw89_complete_cond(struct rtw89_wait_info *wait, unsigned int cond,
+ const struct rtw89_completion_data *data)
+{
+ unsigned int cur;
+
+ cur = atomic_cmpxchg(&wait->cond, cond, RTW89_WAIT_COND_IDLE);
+ if (cur != cond)
+ return;
+
+ wait->data = *data;
+ complete(&wait->completion);
+}
+
int rtw89_core_start(struct rtw89_dev *rtwdev)
{
int ret;
@@ -3062,6 +3130,8 @@ int rtw89_core_init(struct rtw89_dev *rtwdev)
mutex_init(&rtwdev->rf_mutex);
rtwdev->total_sta_assoc = 0;
+ rtw89_init_wait(&rtwdev->mcc.wait);
+
INIT_WORK(&rtwdev->c2h_work, rtw89_fw_c2h_work);
INIT_WORK(&rtwdev->ips_work, rtw89_ips_work);
skb_queue_head_init(&rtwdev->c2h_queue);
@@ -3260,6 +3330,7 @@ static int rtw89_core_register_hw(struct rtw89_dev *rtwdev)
ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS);
ieee80211_hw_set(hw, SINGLE_SCAN_ON_ALL_BANDS);
ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID);
+ ieee80211_hw_set(hw, WANT_MONITOR_VIF);
hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_AP) |
diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index b60de6662548..2badb96d2ae3 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -35,6 +35,7 @@ extern const struct ieee80211_ops rtw89_ops;
#define RSSI_FACTOR 1
#define RTW89_RSSI_RAW_TO_DBM(rssi) ((s8)((rssi) >> RSSI_FACTOR) - MAX_RSSI)
#define RTW89_TX_DIV_RSSI_RAW_TH (2 << RSSI_FACTOR)
+#define RTW89_RADIOTAP_ROOM ALIGN(sizeof(struct ieee80211_radiotap_he), 64)
#define RTW89_HTC_MASK_VARIANT GENMASK(1, 0)
#define RTW89_HTC_VARIANT_HE 3
@@ -2239,6 +2240,8 @@ struct rtw89_phy_rate_pattern {
struct rtw89_vif {
struct list_head list;
struct rtw89_dev *rtwdev;
+ enum rtw89_sub_entity_idx sub_entity_idx;
+
u8 mac_id;
u8 port;
u8 mac_addr[ETH_ALEN];
@@ -2261,6 +2264,8 @@ struct rtw89_vif {
bool wowlan_magic;
bool is_hesta;
bool last_a_ctrl;
+ bool dyn_tb_bedge_en;
+ u8 def_tri_idx;
struct work_struct update_beacon_work;
struct rtw89_addr_cam_entry addr_cam;
struct rtw89_bssid_cam_entry bssid_cam;
@@ -2646,6 +2651,11 @@ struct rtw89_dig_regs {
struct rtw89_reg_def p1_s20_pagcugc_en;
};
+struct rtw89_phy_ul_tb_info {
+ bool dyn_tb_tri_en;
+ u8 def_if_bandedge;
+};
+
struct rtw89_chip_info {
enum rtw89_core_chip_id chip_id;
const struct rtw89_chip_ops *ops;
@@ -2663,6 +2673,7 @@ struct rtw89_chip_info {
u8 support_chanctx_num;
u8 support_bands;
bool support_bw160;
+ bool support_ul_tb_ctrl;
bool hw_sec_hdr;
u8 rf_path_num;
u8 tx_nss;
@@ -2755,6 +2766,7 @@ struct rtw89_chip_info {
u32 c2h_ctrl_reg;
const u32 *c2h_regs;
const struct rtw89_page_regs *page_regs;
+ bool cfo_src_fd;
const struct rtw89_reg_def *dcfo_comp;
u8 dcfo_comp_sft;
const struct rtw89_imr_info *imr_info;
@@ -2802,6 +2814,28 @@ struct rtw89_mac_info {
u8 cpwm_seq_num;
};
+#define RTW89_COMPLETION_BUF_SIZE 24
+#define RTW89_WAIT_COND_IDLE UINT_MAX
+
+struct rtw89_completion_data {
+ bool err;
+ u8 buf[RTW89_COMPLETION_BUF_SIZE];
+};
+
+struct rtw89_wait_info {
+ atomic_t cond;
+ struct completion completion;
+ struct rtw89_completion_data data;
+};
+
+#define RTW89_WAIT_FOR_COND_TIMEOUT msecs_to_jiffies(100)
+
+static inline void rtw89_init_wait(struct rtw89_wait_info *wait)
+{
+ init_completion(&wait->completion);
+ atomic_set(&wait->cond, RTW89_WAIT_COND_IDLE);
+}
+
enum rtw89_fw_type {
RTW89_FW_NORMAL = 1,
RTW89_FW_WOWLAN = 3,
@@ -2921,6 +2955,13 @@ enum rtw89_entity_mode {
RTW89_ENTITY_MODE_SCC,
};
+struct rtw89_sub_entity {
+ struct cfg80211_chan_def chandef;
+ struct rtw89_chan chan;
+ struct rtw89_chan_rcd rcd;
+ struct rtw89_chanctx_cfg *cfg;
+};
+
struct rtw89_hal {
u32 rx_fltr;
u8 cv;
@@ -2934,13 +2975,10 @@ struct rtw89_hal {
bool support_igi;
DECLARE_BITMAP(entity_map, NUM_OF_RTW89_SUB_ENTITY);
- struct cfg80211_chan_def chandef[NUM_OF_RTW89_SUB_ENTITY];
+ struct rtw89_sub_entity sub[NUM_OF_RTW89_SUB_ENTITY];
bool entity_active;
enum rtw89_entity_mode entity_mode;
-
- struct rtw89_chan chan[NUM_OF_RTW89_SUB_ENTITY];
- struct rtw89_chan_rcd chan_rcd[NUM_OF_RTW89_SUB_ENTITY];
};
#define RTW89_MAX_MAC_ID_NUM 128
@@ -2959,6 +2997,7 @@ enum rtw89_flags {
RTW89_FLAG_CRASH_SIMULATING,
RTW89_FLAG_WOWLAN,
RTW89_FLAG_FORBIDDEN_TRACK_WROK,
+ RTW89_FLAG_CHANGING_INTERFACE,
NUM_OF_RTW89_FLAGS,
};
@@ -3021,7 +3060,7 @@ struct rtw89_dack_info {
#define RTW89_IQK_CHS_NR 2
#define RTW89_IQK_PATH_NR 4
-struct rtw89_mcc_info {
+struct rtw89_rfk_mcc_info {
u8 ch[RTW89_IQK_CHS_NR];
u8 band[RTW89_IQK_CHS_NR];
u8 table_idx;
@@ -3514,6 +3553,10 @@ struct rtw89_wow_param {
struct list_head pkt_list;
};
+struct rtw89_mcc_info {
+ struct rtw89_wait_info wait;
+};
+
struct rtw89_dev {
struct ieee80211_hw *hw;
struct device *dev;
@@ -3524,6 +3567,7 @@ struct rtw89_dev {
const struct rtw89_chip_info *chip;
const struct rtw89_pci_info *pci_info;
struct rtw89_hal hal;
+ struct rtw89_mcc_info mcc;
struct rtw89_mac_info mac;
struct rtw89_fw_info fw;
struct rtw89_hci_info hci;
@@ -3567,7 +3611,7 @@ struct rtw89_dev {
struct rtw89_dack_info dack;
struct rtw89_iqk_info iqk;
struct rtw89_dpk_info dpk;
- struct rtw89_mcc_info mcc;
+ struct rtw89_rfk_mcc_info rfk_mcc;
struct rtw89_lck_info lck;
struct rtw89_rx_dck_info rx_dck;
bool is_tssi_mode[RF_PATH_MAX];
@@ -3584,6 +3628,7 @@ struct rtw89_dev {
struct rtw89_phy_ch_info ch_info;
struct rtw89_phy_bb_gain_info bb_gain;
struct rtw89_phy_efuse_gain efuse_gain;
+ struct rtw89_phy_ul_tb_info ul_tb_info;
struct delayed_work track_work;
struct delayed_work coex_act1_work;
@@ -4099,7 +4144,7 @@ const struct cfg80211_chan_def *rtw89_chandef_get(struct rtw89_dev *rtwdev,
{
struct rtw89_hal *hal = &rtwdev->hal;
- return &hal->chandef[idx];
+ return &hal->sub[idx].chandef;
}
static inline
@@ -4108,7 +4153,7 @@ const struct rtw89_chan *rtw89_chan_get(struct rtw89_dev *rtwdev,
{
struct rtw89_hal *hal = &rtwdev->hal;
- return &hal->chan[idx];
+ return &hal->sub[idx].chan;
}
static inline
@@ -4117,7 +4162,7 @@ const struct rtw89_chan_rcd *rtw89_chan_rcd_get(struct rtw89_dev *rtwdev,
{
struct rtw89_hal *hal = &rtwdev->hal;
- return &hal->chan_rcd[idx];
+ return &hal->sub[idx].rcd;
}
static inline void rtw89_chip_fem_setup(struct rtw89_dev *rtwdev)
@@ -4372,6 +4417,23 @@ static inline struct rtw89_fw_suit *rtw89_fw_suit_get(struct rtw89_dev *rtwdev,
return &fw_info->normal;
}
+static inline struct sk_buff *rtw89_alloc_skb_for_rx(struct rtw89_dev *rtwdev,
+ unsigned int length)
+{
+ struct sk_buff *skb;
+
+ if (rtwdev->hw->conf.flags & IEEE80211_CONF_MONITOR) {
+ skb = dev_alloc_skb(length + RTW89_RADIOTAP_ROOM);
+ if (!skb)
+ return NULL;
+
+ skb_reserve(skb, RTW89_RADIOTAP_ROOM);
+ return skb;
+ }
+
+ return dev_alloc_skb(length);
+}
+
int rtw89_core_tx_write(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, struct sk_buff *skb, int *qsel);
int rtw89_h2c_tx(struct rtw89_dev *rtwdev,
@@ -4440,6 +4502,9 @@ int rtw89_regd_init(struct rtw89_dev *rtwdev,
void rtw89_regd_notifier(struct wiphy *wiphy, struct regulatory_request *request);
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);
+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);
diff --git a/drivers/net/wireless/realtek/rtw89/debug.h b/drivers/net/wireless/realtek/rtw89/debug.h
index e7971583acbc..d1de5e600836 100644
--- a/drivers/net/wireless/realtek/rtw89/debug.h
+++ b/drivers/net/wireless/realtek/rtw89/debug.h
@@ -27,6 +27,7 @@ enum rtw89_debug_mask {
RTW89_DBG_SAR = BIT(16),
RTW89_DBG_STATE = BIT(17),
RTW89_DBG_WOW = BIT(18),
+ RTW89_DBG_UL_TB = BIT(19),
RTW89_DBG_UNEXP = BIT(31),
};
diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c
index 10c5ed21da02..7e682709232d 100644
--- a/drivers/net/wireless/realtek/rtw89/fw.c
+++ b/drivers/net/wireless/realtek/rtw89/fw.c
@@ -11,6 +11,9 @@
#include "phy.h"
#include "reg.h"
+static void rtw89_fw_c2h_cmd_handle(struct rtw89_dev *rtwdev,
+ struct sk_buff *skb);
+
static struct sk_buff *rtw89_fw_h2c_alloc_skb(struct rtw89_dev *rtwdev, u32 len,
bool header)
{
@@ -2263,7 +2266,7 @@ fail:
int rtw89_fw_h2c_rf_ntfy_mcc(struct rtw89_dev *rtwdev)
{
const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_SUB_ENTITY_0);
- struct rtw89_mcc_info *mcc_info = &rtwdev->mcc;
+ struct rtw89_rfk_mcc_info *rfk_mcc = &rtwdev->rfk_mcc;
struct rtw89_fw_h2c_rf_get_mccch *mccch;
struct sk_buff *skb;
int ret;
@@ -2276,10 +2279,10 @@ int rtw89_fw_h2c_rf_ntfy_mcc(struct rtw89_dev *rtwdev)
skb_put(skb, sizeof(*mccch));
mccch = (struct rtw89_fw_h2c_rf_get_mccch *)skb->data;
- mccch->ch_0 = cpu_to_le32(mcc_info->ch[0]);
- mccch->ch_1 = cpu_to_le32(mcc_info->ch[1]);
- mccch->band_0 = cpu_to_le32(mcc_info->band[0]);
- mccch->band_1 = cpu_to_le32(mcc_info->band[1]);
+ mccch->ch_0 = cpu_to_le32(rfk_mcc->ch[0]);
+ mccch->ch_1 = cpu_to_le32(rfk_mcc->ch[1]);
+ mccch->band_0 = cpu_to_le32(rfk_mcc->band[0]);
+ mccch->band_1 = cpu_to_le32(rfk_mcc->band[1]);
mccch->current_channel = cpu_to_le32(chan->channel);
mccch->current_band_type = cpu_to_le32(chan->band_type);
@@ -2382,8 +2385,43 @@ void rtw89_fw_free_all_early_h2c(struct rtw89_dev *rtwdev)
mutex_unlock(&rtwdev->mutex);
}
+static void rtw89_fw_c2h_parse_attr(struct sk_buff *c2h)
+{
+ struct rtw89_fw_c2h_attr *attr = RTW89_SKB_C2H_CB(c2h);
+
+ attr->category = RTW89_GET_C2H_CATEGORY(c2h->data);
+ attr->class = RTW89_GET_C2H_CLASS(c2h->data);
+ attr->func = RTW89_GET_C2H_FUNC(c2h->data);
+ attr->len = RTW89_GET_C2H_LEN(c2h->data);
+}
+
+static bool rtw89_fw_c2h_chk_atomic(struct rtw89_dev *rtwdev,
+ struct sk_buff *c2h)
+{
+ struct rtw89_fw_c2h_attr *attr = RTW89_SKB_C2H_CB(c2h);
+ u8 category = attr->category;
+ u8 class = attr->class;
+ u8 func = attr->func;
+
+ switch (category) {
+ default:
+ return false;
+ case RTW89_C2H_CAT_MAC:
+ return rtw89_mac_c2h_chk_atomic(rtwdev, class, func);
+ }
+}
+
void rtw89_fw_c2h_irqsafe(struct rtw89_dev *rtwdev, struct sk_buff *c2h)
{
+ rtw89_fw_c2h_parse_attr(c2h);
+ if (!rtw89_fw_c2h_chk_atomic(rtwdev, c2h))
+ goto enqueue;
+
+ rtw89_fw_c2h_cmd_handle(rtwdev, c2h);
+ dev_kfree_skb_any(c2h);
+ return;
+
+enqueue:
skb_queue_tail(&rtwdev->c2h_queue, c2h);
ieee80211_queue_work(rtwdev->hw, &rtwdev->c2h_work);
}
@@ -2391,10 +2429,11 @@ void rtw89_fw_c2h_irqsafe(struct rtw89_dev *rtwdev, struct sk_buff *c2h)
static void rtw89_fw_c2h_cmd_handle(struct rtw89_dev *rtwdev,
struct sk_buff *skb)
{
- u8 category = RTW89_GET_C2H_CATEGORY(skb->data);
- u8 class = RTW89_GET_C2H_CLASS(skb->data);
- u8 func = RTW89_GET_C2H_FUNC(skb->data);
- u16 len = RTW89_GET_C2H_LEN(skb->data);
+ struct rtw89_fw_c2h_attr *attr = RTW89_SKB_C2H_CB(skb);
+ u8 category = attr->category;
+ u8 class = attr->class;
+ u8 func = attr->func;
+ u16 len = attr->len;
bool dump = true;
if (!test_bit(RTW89_FLAG_RUNNING, rtwdev->flags))
@@ -3224,3 +3263,332 @@ fail:
return ret;
}
+
+static int rtw89_h2c_tx_and_wait(struct rtw89_dev *rtwdev, struct sk_buff *skb,
+ struct rtw89_wait_info *wait, unsigned int cond)
+{
+ int ret;
+
+ ret = rtw89_h2c_tx(rtwdev, skb, false);
+ if (ret) {
+ rtw89_err(rtwdev, "failed to send h2c\n");
+ dev_kfree_skb_any(skb);
+ return -EBUSY;
+ }
+
+ return rtw89_wait_for_cond(wait, cond);
+}
+
+#define H2C_ADD_MCC_LEN 16
+int rtw89_fw_h2c_add_mcc(struct rtw89_dev *rtwdev,
+ const struct rtw89_fw_mcc_add_req *p)
+{
+ struct rtw89_wait_info *wait = &rtwdev->mcc.wait;
+ struct sk_buff *skb;
+ unsigned int cond;
+
+ skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_ADD_MCC_LEN);
+ if (!skb) {
+ rtw89_err(rtwdev,
+ "failed to alloc skb for add mcc\n");
+ return -ENOMEM;
+ }
+
+ skb_put(skb, H2C_ADD_MCC_LEN);
+ RTW89_SET_FWCMD_ADD_MCC_MACID(skb->data, p->macid);
+ RTW89_SET_FWCMD_ADD_MCC_CENTRAL_CH_SEG0(skb->data, p->central_ch_seg0);
+ RTW89_SET_FWCMD_ADD_MCC_CENTRAL_CH_SEG1(skb->data, p->central_ch_seg1);
+ RTW89_SET_FWCMD_ADD_MCC_PRIMARY_CH(skb->data, p->primary_ch);
+ RTW89_SET_FWCMD_ADD_MCC_BANDWIDTH(skb->data, p->bandwidth);
+ RTW89_SET_FWCMD_ADD_MCC_GROUP(skb->data, p->group);
+ RTW89_SET_FWCMD_ADD_MCC_C2H_RPT(skb->data, p->c2h_rpt);
+ RTW89_SET_FWCMD_ADD_MCC_DIS_TX_NULL(skb->data, p->dis_tx_null);
+ RTW89_SET_FWCMD_ADD_MCC_DIS_SW_RETRY(skb->data, p->dis_sw_retry);
+ RTW89_SET_FWCMD_ADD_MCC_IN_CURR_CH(skb->data, p->in_curr_ch);
+ RTW89_SET_FWCMD_ADD_MCC_SW_RETRY_COUNT(skb->data, p->sw_retry_count);
+ RTW89_SET_FWCMD_ADD_MCC_TX_NULL_EARLY(skb->data, p->tx_null_early);
+ RTW89_SET_FWCMD_ADD_MCC_BTC_IN_2G(skb->data, p->btc_in_2g);
+ RTW89_SET_FWCMD_ADD_MCC_PTA_EN(skb->data, p->pta_en);
+ RTW89_SET_FWCMD_ADD_MCC_RFK_BY_PASS(skb->data, p->rfk_by_pass);
+ RTW89_SET_FWCMD_ADD_MCC_CH_BAND_TYPE(skb->data, p->ch_band_type);
+ RTW89_SET_FWCMD_ADD_MCC_DURATION(skb->data, p->duration);
+ RTW89_SET_FWCMD_ADD_MCC_COURTESY_EN(skb->data, p->courtesy_en);
+ RTW89_SET_FWCMD_ADD_MCC_COURTESY_NUM(skb->data, p->courtesy_num);
+ RTW89_SET_FWCMD_ADD_MCC_COURTESY_TARGET(skb->data, p->courtesy_target);
+
+ rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
+ H2C_CAT_MAC,
+ H2C_CL_MCC,
+ H2C_FUNC_ADD_MCC, 0, 0,
+ H2C_ADD_MCC_LEN);
+
+ cond = RTW89_MCC_WAIT_COND(p->group, H2C_FUNC_ADD_MCC);
+ return rtw89_h2c_tx_and_wait(rtwdev, skb, wait, cond);
+}
+
+#define H2C_START_MCC_LEN 12
+int rtw89_fw_h2c_start_mcc(struct rtw89_dev *rtwdev,
+ const struct rtw89_fw_mcc_start_req *p)
+{
+ struct rtw89_wait_info *wait = &rtwdev->mcc.wait;
+ struct sk_buff *skb;
+ unsigned int cond;
+
+ skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_START_MCC_LEN);
+ if (!skb) {
+ rtw89_err(rtwdev,
+ "failed to alloc skb for start mcc\n");
+ return -ENOMEM;
+ }
+
+ skb_put(skb, H2C_START_MCC_LEN);
+ RTW89_SET_FWCMD_START_MCC_GROUP(skb->data, p->group);
+ RTW89_SET_FWCMD_START_MCC_BTC_IN_GROUP(skb->data, p->btc_in_group);
+ RTW89_SET_FWCMD_START_MCC_OLD_GROUP_ACTION(skb->data, p->old_group_action);
+ RTW89_SET_FWCMD_START_MCC_OLD_GROUP(skb->data, p->old_group);
+ RTW89_SET_FWCMD_START_MCC_NOTIFY_CNT(skb->data, p->notify_cnt);
+ RTW89_SET_FWCMD_START_MCC_NOTIFY_RXDBG_EN(skb->data, p->notify_rxdbg_en);
+ RTW89_SET_FWCMD_START_MCC_MACID(skb->data, p->macid);
+ RTW89_SET_FWCMD_START_MCC_TSF_LOW(skb->data, p->tsf_low);
+ RTW89_SET_FWCMD_START_MCC_TSF_HIGH(skb->data, p->tsf_high);
+
+ rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
+ H2C_CAT_MAC,
+ H2C_CL_MCC,
+ H2C_FUNC_START_MCC, 0, 0,
+ H2C_START_MCC_LEN);
+
+ cond = RTW89_MCC_WAIT_COND(p->group, H2C_FUNC_START_MCC);
+ return rtw89_h2c_tx_and_wait(rtwdev, skb, wait, cond);
+}
+
+#define H2C_STOP_MCC_LEN 4
+int rtw89_fw_h2c_stop_mcc(struct rtw89_dev *rtwdev, u8 group, u8 macid,
+ bool prev_groups)
+{
+ struct rtw89_wait_info *wait = &rtwdev->mcc.wait;
+ struct sk_buff *skb;
+ unsigned int cond;
+
+ skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_STOP_MCC_LEN);
+ if (!skb) {
+ rtw89_err(rtwdev,
+ "failed to alloc skb for stop mcc\n");
+ return -ENOMEM;
+ }
+
+ skb_put(skb, H2C_STOP_MCC_LEN);
+ RTW89_SET_FWCMD_STOP_MCC_MACID(skb->data, macid);
+ RTW89_SET_FWCMD_STOP_MCC_GROUP(skb->data, group);
+ RTW89_SET_FWCMD_STOP_MCC_PREV_GROUPS(skb->data, prev_groups);
+
+ rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
+ H2C_CAT_MAC,
+ H2C_CL_MCC,
+ H2C_FUNC_STOP_MCC, 0, 0,
+ H2C_STOP_MCC_LEN);
+
+ cond = RTW89_MCC_WAIT_COND(group, H2C_FUNC_STOP_MCC);
+ return rtw89_h2c_tx_and_wait(rtwdev, skb, wait, cond);
+}
+
+#define H2C_DEL_MCC_GROUP_LEN 4
+int rtw89_fw_h2c_del_mcc_group(struct rtw89_dev *rtwdev, u8 group,
+ bool prev_groups)
+{
+ struct rtw89_wait_info *wait = &rtwdev->mcc.wait;
+ struct sk_buff *skb;
+ unsigned int cond;
+
+ skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_DEL_MCC_GROUP_LEN);
+ if (!skb) {
+ rtw89_err(rtwdev,
+ "failed to alloc skb for del mcc group\n");
+ return -ENOMEM;
+ }
+
+ skb_put(skb, H2C_DEL_MCC_GROUP_LEN);
+ RTW89_SET_FWCMD_DEL_MCC_GROUP_GROUP(skb->data, group);
+ RTW89_SET_FWCMD_DEL_MCC_GROUP_PREV_GROUPS(skb->data, prev_groups);
+
+ rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
+ H2C_CAT_MAC,
+ H2C_CL_MCC,
+ H2C_FUNC_DEL_MCC_GROUP, 0, 0,
+ H2C_DEL_MCC_GROUP_LEN);
+
+ cond = RTW89_MCC_WAIT_COND(group, H2C_FUNC_DEL_MCC_GROUP);
+ return rtw89_h2c_tx_and_wait(rtwdev, skb, wait, cond);
+}
+
+#define H2C_RESET_MCC_GROUP_LEN 4
+int rtw89_fw_h2c_reset_mcc_group(struct rtw89_dev *rtwdev, u8 group)
+{
+ struct rtw89_wait_info *wait = &rtwdev->mcc.wait;
+ struct sk_buff *skb;
+ unsigned int cond;
+
+ skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_RESET_MCC_GROUP_LEN);
+ if (!skb) {
+ rtw89_err(rtwdev,
+ "failed to alloc skb for reset mcc group\n");
+ return -ENOMEM;
+ }
+
+ skb_put(skb, H2C_RESET_MCC_GROUP_LEN);
+ RTW89_SET_FWCMD_RESET_MCC_GROUP_GROUP(skb->data, group);
+
+ rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
+ H2C_CAT_MAC,
+ H2C_CL_MCC,
+ H2C_FUNC_RESET_MCC_GROUP, 0, 0,
+ H2C_RESET_MCC_GROUP_LEN);
+
+ cond = RTW89_MCC_WAIT_COND(group, H2C_FUNC_RESET_MCC_GROUP);
+ return rtw89_h2c_tx_and_wait(rtwdev, skb, wait, cond);
+}
+
+#define H2C_MCC_REQ_TSF_LEN 4
+int rtw89_fw_h2c_mcc_req_tsf(struct rtw89_dev *rtwdev,
+ const struct rtw89_fw_mcc_tsf_req *req,
+ struct rtw89_mac_mcc_tsf_rpt *rpt)
+{
+ struct rtw89_wait_info *wait = &rtwdev->mcc.wait;
+ struct rtw89_mac_mcc_tsf_rpt *tmp;
+ struct sk_buff *skb;
+ unsigned int cond;
+ int ret;
+
+ skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_MCC_REQ_TSF_LEN);
+ if (!skb) {
+ rtw89_err(rtwdev,
+ "failed to alloc skb for mcc req tsf\n");
+ return -ENOMEM;
+ }
+
+ skb_put(skb, H2C_MCC_REQ_TSF_LEN);
+ RTW89_SET_FWCMD_MCC_REQ_TSF_GROUP(skb->data, req->group);
+ RTW89_SET_FWCMD_MCC_REQ_TSF_MACID_X(skb->data, req->macid_x);
+ RTW89_SET_FWCMD_MCC_REQ_TSF_MACID_Y(skb->data, req->macid_y);
+
+ rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
+ H2C_CAT_MAC,
+ H2C_CL_MCC,
+ H2C_FUNC_MCC_REQ_TSF, 0, 0,
+ H2C_MCC_REQ_TSF_LEN);
+
+ cond = RTW89_MCC_WAIT_COND(req->group, H2C_FUNC_MCC_REQ_TSF);
+ ret = rtw89_h2c_tx_and_wait(rtwdev, skb, wait, cond);
+ if (ret)
+ return ret;
+
+ tmp = (struct rtw89_mac_mcc_tsf_rpt *)wait->data.buf;
+ *rpt = *tmp;
+
+ return 0;
+}
+
+#define H2C_MCC_MACID_BITMAP_DSC_LEN 4
+int rtw89_fw_h2c_mcc_macid_bitamp(struct rtw89_dev *rtwdev, u8 group, u8 macid,
+ u8 *bitmap)
+{
+ struct rtw89_wait_info *wait = &rtwdev->mcc.wait;
+ struct sk_buff *skb;
+ unsigned int cond;
+ u8 map_len;
+ u8 h2c_len;
+
+ BUILD_BUG_ON(RTW89_MAX_MAC_ID_NUM % 8);
+ map_len = RTW89_MAX_MAC_ID_NUM / 8;
+ h2c_len = H2C_MCC_MACID_BITMAP_DSC_LEN + map_len;
+ skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, h2c_len);
+ if (!skb) {
+ rtw89_err(rtwdev,
+ "failed to alloc skb for mcc macid bitmap\n");
+ return -ENOMEM;
+ }
+
+ skb_put(skb, h2c_len);
+ RTW89_SET_FWCMD_MCC_MACID_BITMAP_GROUP(skb->data, group);
+ RTW89_SET_FWCMD_MCC_MACID_BITMAP_MACID(skb->data, macid);
+ RTW89_SET_FWCMD_MCC_MACID_BITMAP_BITMAP_LENGTH(skb->data, map_len);
+ RTW89_SET_FWCMD_MCC_MACID_BITMAP_BITMAP(skb->data, bitmap, map_len);
+
+ rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
+ H2C_CAT_MAC,
+ H2C_CL_MCC,
+ H2C_FUNC_MCC_MACID_BITMAP, 0, 0,
+ h2c_len);
+
+ cond = RTW89_MCC_WAIT_COND(group, H2C_FUNC_MCC_MACID_BITMAP);
+ return rtw89_h2c_tx_and_wait(rtwdev, skb, wait, cond);
+}
+
+#define H2C_MCC_SYNC_LEN 4
+int rtw89_fw_h2c_mcc_sync(struct rtw89_dev *rtwdev, u8 group, u8 source,
+ u8 target, u8 offset)
+{
+ struct rtw89_wait_info *wait = &rtwdev->mcc.wait;
+ struct sk_buff *skb;
+ unsigned int cond;
+
+ skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_MCC_SYNC_LEN);
+ if (!skb) {
+ rtw89_err(rtwdev,
+ "failed to alloc skb for mcc sync\n");
+ return -ENOMEM;
+ }
+
+ skb_put(skb, H2C_MCC_SYNC_LEN);
+ RTW89_SET_FWCMD_MCC_SYNC_GROUP(skb->data, group);
+ RTW89_SET_FWCMD_MCC_SYNC_MACID_SOURCE(skb->data, source);
+ RTW89_SET_FWCMD_MCC_SYNC_MACID_TARGET(skb->data, target);
+ RTW89_SET_FWCMD_MCC_SYNC_SYNC_OFFSET(skb->data, offset);
+
+ rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
+ H2C_CAT_MAC,
+ H2C_CL_MCC,
+ H2C_FUNC_MCC_SYNC, 0, 0,
+ H2C_MCC_SYNC_LEN);
+
+ cond = RTW89_MCC_WAIT_COND(group, H2C_FUNC_MCC_SYNC);
+ return rtw89_h2c_tx_and_wait(rtwdev, skb, wait, cond);
+}
+
+#define H2C_MCC_SET_DURATION_LEN 20
+int rtw89_fw_h2c_mcc_set_duration(struct rtw89_dev *rtwdev,
+ const struct rtw89_fw_mcc_duration *p)
+{
+ struct rtw89_wait_info *wait = &rtwdev->mcc.wait;
+ struct sk_buff *skb;
+ unsigned int cond;
+
+ skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_MCC_SET_DURATION_LEN);
+ if (!skb) {
+ rtw89_err(rtwdev,
+ "failed to alloc skb for mcc set duration\n");
+ return -ENOMEM;
+ }
+
+ skb_put(skb, H2C_MCC_SET_DURATION_LEN);
+ RTW89_SET_FWCMD_MCC_SET_DURATION_GROUP(skb->data, p->group);
+ RTW89_SET_FWCMD_MCC_SET_DURATION_BTC_IN_GROUP(skb->data, p->btc_in_group);
+ RTW89_SET_FWCMD_MCC_SET_DURATION_START_MACID(skb->data, p->start_macid);
+ RTW89_SET_FWCMD_MCC_SET_DURATION_MACID_X(skb->data, p->macid_x);
+ RTW89_SET_FWCMD_MCC_SET_DURATION_MACID_Y(skb->data, p->macid_y);
+ RTW89_SET_FWCMD_MCC_SET_DURATION_START_TSF_LOW(skb->data,
+ p->start_tsf_low);
+ RTW89_SET_FWCMD_MCC_SET_DURATION_START_TSF_HIGH(skb->data,
+ p->start_tsf_high);
+ RTW89_SET_FWCMD_MCC_SET_DURATION_DURATION_X(skb->data, p->duration_x);
+ RTW89_SET_FWCMD_MCC_SET_DURATION_DURATION_Y(skb->data, p->duration_y);
+
+ rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
+ H2C_CAT_MAC,
+ H2C_CL_MCC,
+ H2C_FUNC_MCC_SET_DURATION, 0, 0,
+ H2C_MCC_SET_DURATION_LEN);
+
+ cond = RTW89_MCC_WAIT_COND(p->group, H2C_FUNC_MCC_SET_DURATION);
+ return rtw89_h2c_tx_and_wait(rtwdev, skb, wait, cond);
+}
diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h
index 509a3eac5ffe..46d57414f24e 100644
--- a/drivers/net/wireless/realtek/rtw89/fw.h
+++ b/drivers/net/wireless/realtek/rtw89/fw.h
@@ -2767,6 +2767,355 @@ static inline void RTW89_SET_FWCMD_TSF32_TOGL_EARLY(void *cmd, u32 val)
le32p_replace_bits((__le32 *)cmd, val, GENMASK(31, 16));
}
+enum rtw89_fw_mcc_c2h_rpt_cfg {
+ RTW89_FW_MCC_C2H_RPT_OFF = 0,
+ RTW89_FW_MCC_C2H_RPT_FAIL_ONLY = 1,
+ RTW89_FW_MCC_C2H_RPT_ALL = 2,
+};
+
+struct rtw89_fw_mcc_add_req {
+ u8 macid;
+ u8 central_ch_seg0;
+ u8 central_ch_seg1;
+ u8 primary_ch;
+ enum rtw89_bandwidth bandwidth: 4;
+ u32 group: 2;
+ u32 c2h_rpt: 2;
+ u32 dis_tx_null: 1;
+ u32 dis_sw_retry: 1;
+ u32 in_curr_ch: 1;
+ u32 sw_retry_count: 3;
+ u32 tx_null_early: 4;
+ u32 btc_in_2g: 1;
+ u32 pta_en: 1;
+ u32 rfk_by_pass: 1;
+ u32 ch_band_type: 2;
+ u32 rsvd0: 9;
+ u32 duration;
+ u8 courtesy_en;
+ u8 courtesy_num;
+ u8 courtesy_target;
+ u8 rsvd1;
+};
+
+static inline void RTW89_SET_FWCMD_ADD_MCC_MACID(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd, val, GENMASK(7, 0));
+}
+
+static inline void RTW89_SET_FWCMD_ADD_MCC_CENTRAL_CH_SEG0(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd, val, GENMASK(15, 8));
+}
+
+static inline void RTW89_SET_FWCMD_ADD_MCC_CENTRAL_CH_SEG1(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd, val, GENMASK(23, 16));
+}
+
+static inline void RTW89_SET_FWCMD_ADD_MCC_PRIMARY_CH(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd, val, GENMASK(31, 24));
+}
+
+static inline void RTW89_SET_FWCMD_ADD_MCC_BANDWIDTH(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd + 1, val, GENMASK(3, 0));
+}
+
+static inline void RTW89_SET_FWCMD_ADD_MCC_GROUP(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd + 1, val, GENMASK(5, 4));
+}
+
+static inline void RTW89_SET_FWCMD_ADD_MCC_C2H_RPT(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd + 1, val, GENMASK(7, 6));
+}
+
+static inline void RTW89_SET_FWCMD_ADD_MCC_DIS_TX_NULL(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd + 1, val, BIT(8));
+}
+
+static inline void RTW89_SET_FWCMD_ADD_MCC_DIS_SW_RETRY(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd + 1, val, BIT(9));
+}
+
+static inline void RTW89_SET_FWCMD_ADD_MCC_IN_CURR_CH(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd + 1, val, BIT(10));
+}
+
+static inline void RTW89_SET_FWCMD_ADD_MCC_SW_RETRY_COUNT(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd + 1, val, GENMASK(13, 11));
+}
+
+static inline void RTW89_SET_FWCMD_ADD_MCC_TX_NULL_EARLY(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd + 1, val, GENMASK(17, 14));
+}
+
+static inline void RTW89_SET_FWCMD_ADD_MCC_BTC_IN_2G(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd + 1, val, BIT(18));
+}
+
+static inline void RTW89_SET_FWCMD_ADD_MCC_PTA_EN(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd + 1, val, BIT(19));
+}
+
+static inline void RTW89_SET_FWCMD_ADD_MCC_RFK_BY_PASS(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd + 1, val, BIT(20));
+}
+
+static inline void RTW89_SET_FWCMD_ADD_MCC_CH_BAND_TYPE(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd + 1, val, GENMASK(22, 21));
+}
+
+static inline void RTW89_SET_FWCMD_ADD_MCC_DURATION(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd + 2, val, GENMASK(31, 0));
+}
+
+static inline void RTW89_SET_FWCMD_ADD_MCC_COURTESY_EN(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd + 3, val, BIT(0));
+}
+
+static inline void RTW89_SET_FWCMD_ADD_MCC_COURTESY_NUM(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd + 3, val, GENMASK(15, 8));
+}
+
+static inline void RTW89_SET_FWCMD_ADD_MCC_COURTESY_TARGET(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd + 3, val, GENMASK(23, 16));
+}
+
+struct rtw89_fw_mcc_start_req {
+ u32 group: 2;
+ u32 btc_in_group: 1;
+ u32 old_group_action: 2;
+ u32 old_group: 2;
+ u32 rsvd0: 9;
+ u32 notify_cnt: 3;
+ u32 rsvd1: 2;
+ u32 notify_rxdbg_en: 1;
+ u32 rsvd2: 2;
+ u32 macid: 8;
+ u32 tsf_low;
+ u32 tsf_high;
+};
+
+static inline void RTW89_SET_FWCMD_START_MCC_GROUP(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd, val, GENMASK(1, 0));
+}
+
+static inline void RTW89_SET_FWCMD_START_MCC_BTC_IN_GROUP(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd, val, BIT(2));
+}
+
+static inline void RTW89_SET_FWCMD_START_MCC_OLD_GROUP_ACTION(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd, val, GENMASK(4, 3));
+}
+
+static inline void RTW89_SET_FWCMD_START_MCC_OLD_GROUP(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd, val, GENMASK(6, 5));
+}
+
+static inline void RTW89_SET_FWCMD_START_MCC_NOTIFY_CNT(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd, val, GENMASK(18, 16));
+}
+
+static inline void RTW89_SET_FWCMD_START_MCC_NOTIFY_RXDBG_EN(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd, val, BIT(21));
+}
+
+static inline void RTW89_SET_FWCMD_START_MCC_MACID(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd, val, GENMASK(31, 24));
+}
+
+static inline void RTW89_SET_FWCMD_START_MCC_TSF_LOW(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd + 1, val, GENMASK(31, 0));
+}
+
+static inline void RTW89_SET_FWCMD_START_MCC_TSF_HIGH(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd + 2, val, GENMASK(31, 0));
+}
+
+static inline void RTW89_SET_FWCMD_STOP_MCC_MACID(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd, val, GENMASK(7, 0));
+}
+
+static inline void RTW89_SET_FWCMD_STOP_MCC_GROUP(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd, val, GENMASK(9, 8));
+}
+
+static inline void RTW89_SET_FWCMD_STOP_MCC_PREV_GROUPS(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd, val, BIT(10));
+}
+
+static inline void RTW89_SET_FWCMD_DEL_MCC_GROUP_GROUP(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd, val, GENMASK(1, 0));
+}
+
+static inline void RTW89_SET_FWCMD_DEL_MCC_GROUP_PREV_GROUPS(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd, val, BIT(2));
+}
+
+static inline void RTW89_SET_FWCMD_RESET_MCC_GROUP_GROUP(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd, val, GENMASK(1, 0));
+}
+
+struct rtw89_fw_mcc_tsf_req {
+ u8 group: 2;
+ u8 rsvd0: 6;
+ u8 macid_x;
+ u8 macid_y;
+ u8 rsvd1;
+};
+
+static inline void RTW89_SET_FWCMD_MCC_REQ_TSF_GROUP(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd, val, GENMASK(1, 0));
+}
+
+static inline void RTW89_SET_FWCMD_MCC_REQ_TSF_MACID_X(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd, val, GENMASK(15, 8));
+}
+
+static inline void RTW89_SET_FWCMD_MCC_REQ_TSF_MACID_Y(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd, val, GENMASK(23, 16));
+}
+
+static inline void RTW89_SET_FWCMD_MCC_MACID_BITMAP_GROUP(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd, val, GENMASK(1, 0));
+}
+
+static inline void RTW89_SET_FWCMD_MCC_MACID_BITMAP_MACID(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd, val, GENMASK(15, 8));
+}
+
+static inline void RTW89_SET_FWCMD_MCC_MACID_BITMAP_BITMAP_LENGTH(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd, val, GENMASK(23, 16));
+}
+
+static inline void RTW89_SET_FWCMD_MCC_MACID_BITMAP_BITMAP(void *cmd,
+ u8 *bitmap, u8 len)
+{
+ memcpy((__le32 *)cmd + 1, bitmap, len);
+}
+
+static inline void RTW89_SET_FWCMD_MCC_SYNC_GROUP(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd, val, GENMASK(1, 0));
+}
+
+static inline void RTW89_SET_FWCMD_MCC_SYNC_MACID_SOURCE(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd, val, GENMASK(15, 8));
+}
+
+static inline void RTW89_SET_FWCMD_MCC_SYNC_MACID_TARGET(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd, val, GENMASK(23, 16));
+}
+
+static inline void RTW89_SET_FWCMD_MCC_SYNC_SYNC_OFFSET(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd, val, GENMASK(31, 24));
+}
+
+struct rtw89_fw_mcc_duration {
+ u32 group: 2;
+ u32 btc_in_group: 1;
+ u32 rsvd0: 5;
+ u32 start_macid: 8;
+ u32 macid_x: 8;
+ u32 macid_y: 8;
+ u32 start_tsf_low;
+ u32 start_tsf_high;
+ u32 duration_x;
+ u32 duration_y;
+};
+
+static inline void RTW89_SET_FWCMD_MCC_SET_DURATION_GROUP(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd, val, GENMASK(1, 0));
+}
+
+static
+inline void RTW89_SET_FWCMD_MCC_SET_DURATION_BTC_IN_GROUP(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd, val, BIT(2));
+}
+
+static
+inline void RTW89_SET_FWCMD_MCC_SET_DURATION_START_MACID(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd, val, GENMASK(15, 8));
+}
+
+static inline void RTW89_SET_FWCMD_MCC_SET_DURATION_MACID_X(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd, val, GENMASK(23, 16));
+}
+
+static inline void RTW89_SET_FWCMD_MCC_SET_DURATION_MACID_Y(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd, val, GENMASK(31, 24));
+}
+
+static
+inline void RTW89_SET_FWCMD_MCC_SET_DURATION_START_TSF_LOW(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd + 1, val, GENMASK(31, 0));
+}
+
+static
+inline void RTW89_SET_FWCMD_MCC_SET_DURATION_START_TSF_HIGH(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd + 2, val, GENMASK(31, 0));
+}
+
+static
+inline void RTW89_SET_FWCMD_MCC_SET_DURATION_DURATION_X(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd + 3, val, GENMASK(31, 0));
+}
+
+static
+inline void RTW89_SET_FWCMD_MCC_SET_DURATION_DURATION_Y(void *cmd, u32 val)
+{
+ le32p_replace_bits((__le32 *)cmd + 4, val, GENMASK(31, 0));
+}
+
#define RTW89_C2H_HEADER_LEN 8
#define RTW89_GET_C2H_CATEGORY(c2h) \
@@ -2778,6 +3127,20 @@ static inline void RTW89_SET_FWCMD_TSF32_TOGL_EARLY(void *cmd, u32 val)
#define RTW89_GET_C2H_LEN(c2h) \
le32_get_bits(*((const __le32 *)(c2h) + 1), GENMASK(13, 0))
+struct rtw89_fw_c2h_attr {
+ u8 category;
+ u8 class;
+ u8 func;
+ u16 len;
+};
+
+static inline struct rtw89_fw_c2h_attr *RTW89_SKB_C2H_CB(struct sk_buff *skb)
+{
+ static_assert(sizeof(skb->cb) >= sizeof(struct rtw89_fw_c2h_attr));
+
+ return (struct rtw89_fw_c2h_attr *)skb->cb;
+}
+
#define RTW89_GET_C2H_LOG_SRT_PRT(c2h) (char *)((__le32 *)(c2h) + 2)
#define RTW89_GET_C2H_LOG_LEN(len) ((len) - RTW89_C2H_HEADER_LEN)
@@ -2845,6 +3208,55 @@ static inline void RTW89_SET_FWCMD_TSF32_TOGL_EARLY(void *cmd, u32 val)
#define RTW89_GET_MAC_C2H_SCANOFLD_BAND(c2h) \
le32_get_bits(*((const __le32 *)(c2h) + 5), GENMASK(25, 24))
+#define RTW89_GET_MAC_C2H_MCC_RCV_ACK_GROUP(c2h) \
+ le32_get_bits(*((const __le32 *)(c2h)), GENMASK(1, 0))
+#define RTW89_GET_MAC_C2H_MCC_RCV_ACK_H2C_FUNC(c2h) \
+ le32_get_bits(*((const __le32 *)(c2h)), GENMASK(15, 8))
+
+#define RTW89_GET_MAC_C2H_MCC_REQ_ACK_GROUP(c2h) \
+ le32_get_bits(*((const __le32 *)(c2h)), GENMASK(1, 0))
+#define RTW89_GET_MAC_C2H_MCC_REQ_ACK_H2C_RETURN(c2h) \
+ le32_get_bits(*((const __le32 *)(c2h)), GENMASK(7, 2))
+#define RTW89_GET_MAC_C2H_MCC_REQ_ACK_H2C_FUNC(c2h) \
+ le32_get_bits(*((const __le32 *)(c2h)), GENMASK(15, 8))
+
+struct rtw89_mac_mcc_tsf_rpt {
+ u32 macid_x;
+ u32 macid_y;
+ u32 tsf_x_low;
+ u32 tsf_x_high;
+ u32 tsf_y_low;
+ u32 tsf_y_high;
+};
+
+static_assert(sizeof(struct rtw89_mac_mcc_tsf_rpt) <= RTW89_COMPLETION_BUF_SIZE);
+
+#define RTW89_GET_MAC_C2H_MCC_TSF_RPT_MACID_X(c2h) \
+ le32_get_bits(*((const __le32 *)(c2h)), GENMASK(7, 0))
+#define RTW89_GET_MAC_C2H_MCC_TSF_RPT_MACID_Y(c2h) \
+ le32_get_bits(*((const __le32 *)(c2h)), GENMASK(15, 8))
+#define RTW89_GET_MAC_C2H_MCC_TSF_RPT_GROUP(c2h) \
+ le32_get_bits(*((const __le32 *)(c2h)), GENMASK(17, 16))
+#define RTW89_GET_MAC_C2H_MCC_TSF_RPT_TSF_LOW_X(c2h) \
+ le32_get_bits(*((const __le32 *)(c2h) + 1), GENMASK(31, 0))
+#define RTW89_GET_MAC_C2H_MCC_TSF_RPT_TSF_HIGH_X(c2h) \
+ le32_get_bits(*((const __le32 *)(c2h) + 2), GENMASK(31, 0))
+#define RTW89_GET_MAC_C2H_MCC_TSF_RPT_TSF_LOW_Y(c2h) \
+ le32_get_bits(*((const __le32 *)(c2h) + 3), GENMASK(31, 0))
+#define RTW89_GET_MAC_C2H_MCC_TSF_RPT_TSF_HIGH_Y(c2h) \
+ le32_get_bits(*((const __le32 *)(c2h) + 4), GENMASK(31, 0))
+
+#define RTW89_GET_MAC_C2H_MCC_STATUS_RPT_STATUS(c2h) \
+ le32_get_bits(*((const __le32 *)(c2h)), GENMASK(5, 0))
+#define RTW89_GET_MAC_C2H_MCC_STATUS_RPT_GROUP(c2h) \
+ le32_get_bits(*((const __le32 *)(c2h)), GENMASK(7, 6))
+#define RTW89_GET_MAC_C2H_MCC_STATUS_RPT_MACID(c2h) \
+ le32_get_bits(*((const __le32 *)(c2h)), GENMASK(15, 8))
+#define RTW89_GET_MAC_C2H_MCC_STATUS_RPT_TSF_LOW(c2h) \
+ le32_get_bits(*((const __le32 *)(c2h) + 1), GENMASK(31, 0))
+#define RTW89_GET_MAC_C2H_MCC_STATUS_RPT_TSF_HIGH(c2h) \
+ le32_get_bits(*((const __le32 *)(c2h) + 2), GENMASK(31, 0))
+
#define RTW89_FW_HDR_SIZE 32
#define RTW89_FW_SECTION_HDR_SIZE 16
@@ -2966,6 +3378,25 @@ struct rtw89_fw_h2c_rf_reg_info {
#define H2C_CL_BA_CAM 0xc
#define H2C_FUNC_MAC_BA_CAM 0x0
+/* CLASS 14 - MCC */
+#define H2C_CL_MCC 0xe
+enum rtw89_mcc_h2c_func {
+ H2C_FUNC_ADD_MCC = 0x0,
+ H2C_FUNC_START_MCC = 0x1,
+ H2C_FUNC_STOP_MCC = 0x2,
+ H2C_FUNC_DEL_MCC_GROUP = 0x3,
+ H2C_FUNC_RESET_MCC_GROUP = 0x4,
+ H2C_FUNC_MCC_REQ_TSF = 0x5,
+ H2C_FUNC_MCC_MACID_BITMAP = 0x6,
+ H2C_FUNC_MCC_SYNC = 0x7,
+ H2C_FUNC_MCC_SET_DURATION = 0x8,
+
+ NUM_OF_RTW89_MCC_H2C_FUNC,
+};
+
+#define RTW89_MCC_WAIT_COND(group, func) \
+ ((group) * NUM_OF_RTW89_MCC_H2C_FUNC + (func))
+
#define H2C_CAT_OUTSRC 0x2
#define H2C_CL_OUTSRC_RA 0x1
@@ -3103,9 +3534,27 @@ int rtw89_fw_h2c_wow_global(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif,
bool enable);
int rtw89_fw_h2c_wow_wakeup_ctrl(struct rtw89_dev *rtwdev,
struct rtw89_vif *rtwvif, bool enable);
-
int rtw89_fw_wow_cam_update(struct rtw89_dev *rtwdev,
struct rtw89_wow_cam_info *cam_info);
+int rtw89_fw_h2c_add_mcc(struct rtw89_dev *rtwdev,
+ const struct rtw89_fw_mcc_add_req *p);
+int rtw89_fw_h2c_start_mcc(struct rtw89_dev *rtwdev,
+ const struct rtw89_fw_mcc_start_req *p);
+int rtw89_fw_h2c_stop_mcc(struct rtw89_dev *rtwdev, u8 group, u8 macid,
+ bool prev_groups);
+int rtw89_fw_h2c_del_mcc_group(struct rtw89_dev *rtwdev, u8 group,
+ bool prev_groups);
+int rtw89_fw_h2c_reset_mcc_group(struct rtw89_dev *rtwdev, u8 group);
+int rtw89_fw_h2c_mcc_req_tsf(struct rtw89_dev *rtwdev,
+ const struct rtw89_fw_mcc_tsf_req *req,
+ struct rtw89_mac_mcc_tsf_rpt *rpt);
+int rtw89_fw_h2c_mcc_macid_bitamp(struct rtw89_dev *rtwdev, u8 group, u8 macid,
+ u8 *bitmap);
+int rtw89_fw_h2c_mcc_sync(struct rtw89_dev *rtwdev, u8 group, u8 source,
+ u8 target, u8 offset);
+int rtw89_fw_h2c_mcc_set_duration(struct rtw89_dev *rtwdev,
+ const struct rtw89_fw_mcc_duration *p);
+
static inline void rtw89_fw_h2c_init_ba_cam(struct rtw89_dev *rtwdev)
{
const struct rtw89_chip_info *chip = rtwdev->chip;
diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c
index ecd603a88134..d80050c2e9b3 100644
--- a/drivers/net/wireless/realtek/rtw89/mac.c
+++ b/drivers/net/wireless/realtek/rtw89/mac.c
@@ -3600,6 +3600,13 @@ int rtw89_mac_set_macid_pause(struct rtw89_dev *rtwdev, u8 macid, bool pause)
u8 grp = macid >> 5;
int ret;
+ /* If this is called by change_interface() in the case of P2P, it could
+ * be power-off, so ignore this operation.
+ */
+ if (test_bit(RTW89_FLAG_CHANGING_INTERFACE, rtwdev->flags) &&
+ !test_bit(RTW89_FLAG_POWERON, rtwdev->flags))
+ return 0;
+
ret = rtw89_mac_check_mac_en(rtwdev, RTW89_MAC_0, RTW89_CMAC_SEL);
if (ret)
return ret;
@@ -4180,6 +4187,164 @@ rtw89_mac_c2h_tsf32_toggle_rpt(struct rtw89_dev *rtwdev, struct sk_buff *c2h,
{
}
+static void
+rtw89_mac_c2h_mcc_rcv_ack(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len)
+{
+ u8 group = RTW89_GET_MAC_C2H_MCC_RCV_ACK_GROUP(c2h->data);
+ u8 func = RTW89_GET_MAC_C2H_MCC_RCV_ACK_H2C_FUNC(c2h->data);
+
+ switch (func) {
+ case H2C_FUNC_ADD_MCC:
+ case H2C_FUNC_START_MCC:
+ case H2C_FUNC_STOP_MCC:
+ case H2C_FUNC_DEL_MCC_GROUP:
+ case H2C_FUNC_RESET_MCC_GROUP:
+ case H2C_FUNC_MCC_REQ_TSF:
+ case H2C_FUNC_MCC_MACID_BITMAP:
+ case H2C_FUNC_MCC_SYNC:
+ case H2C_FUNC_MCC_SET_DURATION:
+ break;
+ default:
+ rtw89_debug(rtwdev, RTW89_DBG_FW,
+ "invalid MCC C2H RCV ACK: func %d\n", func);
+ return;
+ }
+
+ rtw89_debug(rtwdev, RTW89_DBG_FW,
+ "MCC C2H RCV ACK: group %d, func %d\n", group, func);
+}
+
+static void
+rtw89_mac_c2h_mcc_req_ack(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len)
+{
+ u8 group = RTW89_GET_MAC_C2H_MCC_REQ_ACK_GROUP(c2h->data);
+ u8 func = RTW89_GET_MAC_C2H_MCC_REQ_ACK_H2C_FUNC(c2h->data);
+ u8 retcode = RTW89_GET_MAC_C2H_MCC_REQ_ACK_H2C_RETURN(c2h->data);
+ struct rtw89_completion_data data = {};
+ unsigned int cond;
+ bool next = false;
+
+ switch (func) {
+ case H2C_FUNC_MCC_REQ_TSF:
+ next = true;
+ break;
+ case H2C_FUNC_MCC_MACID_BITMAP:
+ case H2C_FUNC_MCC_SYNC:
+ case H2C_FUNC_MCC_SET_DURATION:
+ break;
+ case H2C_FUNC_ADD_MCC:
+ case H2C_FUNC_START_MCC:
+ case H2C_FUNC_STOP_MCC:
+ case H2C_FUNC_DEL_MCC_GROUP:
+ case H2C_FUNC_RESET_MCC_GROUP:
+ default:
+ rtw89_debug(rtwdev, RTW89_DBG_FW,
+ "invalid MCC C2H REQ ACK: func %d\n", func);
+ return;
+ }
+
+ rtw89_debug(rtwdev, RTW89_DBG_FW,
+ "MCC C2H REQ ACK: group %d, func %d, return code %d\n",
+ group, func, retcode);
+
+ if (!retcode && next)
+ return;
+
+ data.err = !!retcode;
+ cond = RTW89_MCC_WAIT_COND(group, func);
+ rtw89_complete_cond(&rtwdev->mcc.wait, cond, &data);
+}
+
+static void
+rtw89_mac_c2h_mcc_tsf_rpt(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len)
+{
+ u8 group = RTW89_GET_MAC_C2H_MCC_TSF_RPT_GROUP(c2h->data);
+ struct rtw89_completion_data data = {};
+ struct rtw89_mac_mcc_tsf_rpt *rpt;
+ unsigned int cond;
+
+ rpt = (struct rtw89_mac_mcc_tsf_rpt *)data.buf;
+ rpt->macid_x = RTW89_GET_MAC_C2H_MCC_TSF_RPT_MACID_X(c2h->data);
+ rpt->macid_y = RTW89_GET_MAC_C2H_MCC_TSF_RPT_MACID_Y(c2h->data);
+ rpt->tsf_x_low = RTW89_GET_MAC_C2H_MCC_TSF_RPT_TSF_LOW_X(c2h->data);
+ rpt->tsf_x_high = RTW89_GET_MAC_C2H_MCC_TSF_RPT_TSF_HIGH_X(c2h->data);
+ rpt->tsf_y_low = RTW89_GET_MAC_C2H_MCC_TSF_RPT_TSF_LOW_Y(c2h->data);
+ rpt->tsf_y_high = RTW89_GET_MAC_C2H_MCC_TSF_RPT_TSF_HIGH_Y(c2h->data);
+
+ cond = RTW89_MCC_WAIT_COND(group, H2C_FUNC_MCC_REQ_TSF);
+ rtw89_complete_cond(&rtwdev->mcc.wait, cond, &data);
+}
+
+static void
+rtw89_mac_c2h_mcc_status_rpt(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len)
+{
+ u8 group = RTW89_GET_MAC_C2H_MCC_STATUS_RPT_GROUP(c2h->data);
+ u8 macid = RTW89_GET_MAC_C2H_MCC_STATUS_RPT_MACID(c2h->data);
+ u8 status = RTW89_GET_MAC_C2H_MCC_STATUS_RPT_STATUS(c2h->data);
+ u32 tsf_low = RTW89_GET_MAC_C2H_MCC_STATUS_RPT_TSF_LOW(c2h->data);
+ u32 tsf_high = RTW89_GET_MAC_C2H_MCC_STATUS_RPT_TSF_HIGH(c2h->data);
+ struct rtw89_completion_data data = {};
+ unsigned int cond;
+ bool rsp = true;
+ bool err;
+ u8 func;
+
+ switch (status) {
+ case RTW89_MAC_MCC_ADD_ROLE_OK:
+ case RTW89_MAC_MCC_ADD_ROLE_FAIL:
+ func = H2C_FUNC_ADD_MCC;
+ err = status == RTW89_MAC_MCC_ADD_ROLE_FAIL;
+ break;
+ case RTW89_MAC_MCC_START_GROUP_OK:
+ case RTW89_MAC_MCC_START_GROUP_FAIL:
+ func = H2C_FUNC_START_MCC;
+ err = status == RTW89_MAC_MCC_START_GROUP_FAIL;
+ break;
+ case RTW89_MAC_MCC_STOP_GROUP_OK:
+ case RTW89_MAC_MCC_STOP_GROUP_FAIL:
+ func = H2C_FUNC_STOP_MCC;
+ err = status == RTW89_MAC_MCC_STOP_GROUP_FAIL;
+ break;
+ case RTW89_MAC_MCC_DEL_GROUP_OK:
+ case RTW89_MAC_MCC_DEL_GROUP_FAIL:
+ func = H2C_FUNC_DEL_MCC_GROUP;
+ err = status == RTW89_MAC_MCC_DEL_GROUP_FAIL;
+ break;
+ case RTW89_MAC_MCC_RESET_GROUP_OK:
+ case RTW89_MAC_MCC_RESET_GROUP_FAIL:
+ func = H2C_FUNC_RESET_MCC_GROUP;
+ err = status == RTW89_MAC_MCC_RESET_GROUP_FAIL;
+ break;
+ case RTW89_MAC_MCC_SWITCH_CH_OK:
+ case RTW89_MAC_MCC_SWITCH_CH_FAIL:
+ case RTW89_MAC_MCC_TXNULL0_OK:
+ case RTW89_MAC_MCC_TXNULL0_FAIL:
+ case RTW89_MAC_MCC_TXNULL1_OK:
+ case RTW89_MAC_MCC_TXNULL1_FAIL:
+ case RTW89_MAC_MCC_SWITCH_EARLY:
+ case RTW89_MAC_MCC_TBTT:
+ case RTW89_MAC_MCC_DURATION_START:
+ case RTW89_MAC_MCC_DURATION_END:
+ rsp = false;
+ break;
+ default:
+ rtw89_debug(rtwdev, RTW89_DBG_FW,
+ "invalid MCC C2H STS RPT: status %d\n", status);
+ return;
+ }
+
+ rtw89_debug(rtwdev, RTW89_DBG_FW,
+ "MCC C2H STS RPT: group %d, macid %d, status %d, tsf {%d, %d}\n",
+ group, macid, status, tsf_low, tsf_high);
+
+ if (!rsp)
+ return;
+
+ data.err = err;
+ cond = RTW89_MCC_WAIT_COND(group, func);
+ rtw89_complete_cond(&rtwdev->mcc.wait, cond, &data);
+}
+
static
void (* const rtw89_mac_c2h_ofld_handler[])(struct rtw89_dev *rtwdev,
struct sk_buff *c2h, u32 len) = {
@@ -4201,6 +4366,25 @@ void (* const rtw89_mac_c2h_info_handler[])(struct rtw89_dev *rtwdev,
[RTW89_MAC_C2H_FUNC_BCN_CNT] = rtw89_mac_c2h_bcn_cnt,
};
+static
+void (* const rtw89_mac_c2h_mcc_handler[])(struct rtw89_dev *rtwdev,
+ struct sk_buff *c2h, u32 len) = {
+ [RTW89_MAC_C2H_FUNC_MCC_RCV_ACK] = rtw89_mac_c2h_mcc_rcv_ack,
+ [RTW89_MAC_C2H_FUNC_MCC_REQ_ACK] = rtw89_mac_c2h_mcc_req_ack,
+ [RTW89_MAC_C2H_FUNC_MCC_TSF_RPT] = rtw89_mac_c2h_mcc_tsf_rpt,
+ [RTW89_MAC_C2H_FUNC_MCC_STATUS_RPT] = rtw89_mac_c2h_mcc_status_rpt,
+};
+
+bool rtw89_mac_c2h_chk_atomic(struct rtw89_dev *rtwdev, u8 class, u8 func)
+{
+ switch (class) {
+ default:
+ return false;
+ case RTW89_MAC_C2H_CLASS_MCC:
+ return true;
+ }
+}
+
void rtw89_mac_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb,
u32 len, u8 class, u8 func)
{
@@ -4216,6 +4400,10 @@ void rtw89_mac_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb,
if (func < RTW89_MAC_C2H_FUNC_OFLD_MAX)
handler = rtw89_mac_c2h_ofld_handler[func];
break;
+ case RTW89_MAC_C2H_CLASS_MCC:
+ if (func < NUM_OF_RTW89_MAC_C2H_FUNC_MCC)
+ handler = rtw89_mac_c2h_mcc_handler[func];
+ break;
case RTW89_MAC_C2H_CLASS_FWDBG:
return;
default:
diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h
index 045e8ec61a41..adb0c86a98d3 100644
--- a/drivers/net/wireless/realtek/rtw89/mac.h
+++ b/drivers/net/wireless/realtek/rtw89/mac.h
@@ -368,6 +368,15 @@ enum rtw89_mac_c2h_info_func {
RTW89_MAC_C2H_FUNC_INFO_MAX,
};
+enum rtw89_mac_c2h_mcc_func {
+ RTW89_MAC_C2H_FUNC_MCC_RCV_ACK = 0,
+ RTW89_MAC_C2H_FUNC_MCC_REQ_ACK = 1,
+ RTW89_MAC_C2H_FUNC_MCC_TSF_RPT = 2,
+ RTW89_MAC_C2H_FUNC_MCC_STATUS_RPT = 3,
+
+ NUM_OF_RTW89_MAC_C2H_FUNC_MCC,
+};
+
enum rtw89_mac_c2h_class {
RTW89_MAC_C2H_CLASS_INFO,
RTW89_MAC_C2H_CLASS_OFLD,
@@ -378,6 +387,31 @@ enum rtw89_mac_c2h_class {
RTW89_MAC_C2H_CLASS_MAX,
};
+enum rtw89_mac_mcc_status {
+ RTW89_MAC_MCC_ADD_ROLE_OK = 0,
+ RTW89_MAC_MCC_START_GROUP_OK = 1,
+ RTW89_MAC_MCC_STOP_GROUP_OK = 2,
+ RTW89_MAC_MCC_DEL_GROUP_OK = 3,
+ RTW89_MAC_MCC_RESET_GROUP_OK = 4,
+ RTW89_MAC_MCC_SWITCH_CH_OK = 5,
+ RTW89_MAC_MCC_TXNULL0_OK = 6,
+ RTW89_MAC_MCC_TXNULL1_OK = 7,
+
+ RTW89_MAC_MCC_SWITCH_EARLY = 10,
+ RTW89_MAC_MCC_TBTT = 11,
+ RTW89_MAC_MCC_DURATION_START = 12,
+ RTW89_MAC_MCC_DURATION_END = 13,
+
+ RTW89_MAC_MCC_ADD_ROLE_FAIL = 20,
+ RTW89_MAC_MCC_START_GROUP_FAIL = 21,
+ RTW89_MAC_MCC_STOP_GROUP_FAIL = 22,
+ RTW89_MAC_MCC_DEL_GROUP_FAIL = 23,
+ RTW89_MAC_MCC_RESET_GROUP_FAIL = 24,
+ RTW89_MAC_MCC_SWITCH_CH_FAIL = 25,
+ RTW89_MAC_MCC_TXNULL0_FAIL = 26,
+ RTW89_MAC_MCC_TXNULL1_FAIL = 27,
+};
+
struct rtw89_mac_ax_coex {
#define RTW89_MAC_AX_COEX_RTK_MODE 0
#define RTW89_MAC_AX_COEX_CSR_MODE 1
@@ -894,6 +928,7 @@ static inline int rtw89_chip_disable_bb_rf(struct rtw89_dev *rtwdev)
u32 rtw89_mac_get_err_status(struct rtw89_dev *rtwdev);
int rtw89_mac_set_err_status(struct rtw89_dev *rtwdev, u32 err);
+bool rtw89_mac_c2h_chk_atomic(struct rtw89_dev *rtwdev, u8 class, u8 func);
void rtw89_mac_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb,
u32 len, u8 class, u8 func);
int rtw89_mac_setup_phycap(struct rtw89_dev *rtwdev);
diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c
index 6e79bf899901..1a99267d710d 100644
--- a/drivers/net/wireless/realtek/rtw89/mac80211.c
+++ b/drivers/net/wireless/realtek/rtw89/mac80211.c
@@ -131,6 +131,7 @@ static int rtw89_ops_add_interface(struct ieee80211_hw *hw,
rtwvif->bcn_hit_cond = 0;
rtwvif->mac_idx = RTW89_MAC_0;
rtwvif->phy_idx = RTW89_PHY_0;
+ rtwvif->sub_entity_idx = RTW89_SUB_ENTITY_0;
rtwvif->hit_rule = 0;
ether_addr_copy(rtwvif->mac_addr, vif->addr);
@@ -174,6 +175,9 @@ static int rtw89_ops_change_interface(struct ieee80211_hw *hw,
enum nl80211_iftype type, bool p2p)
{
struct rtw89_dev *rtwdev = hw->priv;
+ int ret;
+
+ set_bit(RTW89_FLAG_CHANGING_INTERFACE, rtwdev->flags);
rtw89_debug(rtwdev, RTW89_DBG_STATE, "change vif %pM (%d)->(%d), p2p (%d)->(%d)\n",
vif->addr, vif->type, type, vif->p2p, p2p);
@@ -183,7 +187,13 @@ static int rtw89_ops_change_interface(struct ieee80211_hw *hw,
vif->type = type;
vif->p2p = p2p;
- return rtw89_ops_add_interface(hw, vif);
+ ret = rtw89_ops_add_interface(hw, vif);
+ if (ret)
+ rtw89_warn(rtwdev, "failed to change interface %d\n", ret);
+
+ clear_bit(RTW89_FLAG_CHANGING_INTERFACE, rtwdev->flags);
+
+ return ret;
}
static void rtw89_ops_configure_filter(struct ieee80211_hw *hw,
diff --git a/drivers/net/wireless/realtek/rtw89/pci.c b/drivers/net/wireless/realtek/rtw89/pci.c
index 7aa0af18cdd5..1c4500ba777c 100644
--- a/drivers/net/wireless/realtek/rtw89/pci.c
+++ b/drivers/net/wireless/realtek/rtw89/pci.c
@@ -267,7 +267,7 @@ static u32 rtw89_pci_rxbd_deliver_skbs(struct rtw89_dev *rtwdev,
rtw89_core_query_rxdesc(rtwdev, desc_info, skb->data, rxinfo_size);
- new = dev_alloc_skb(desc_info->pkt_size);
+ new = rtw89_alloc_skb_for_rx(rtwdev, desc_info->pkt_size);
if (!new)
goto err_sync_device;
diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c
index dd4685698983..017710c580c7 100644
--- a/drivers/net/wireless/realtek/rtw89/phy.c
+++ b/drivers/net/wireless/realtek/rtw89/phy.c
@@ -2,6 +2,7 @@
/* Copyright(c) 2019-2020 Realtek Corporation
*/
+#include "coex.h"
#include "debug.h"
#include "fw.h"
#include "mac.h"
@@ -9,7 +10,7 @@
#include "ps.h"
#include "reg.h"
#include "sar.h"
-#include "coex.h"
+#include "util.h"
static u16 get_max_amsdu_len(struct rtw89_dev *rtwdev,
const struct rtw89_ra_report *report)
@@ -2794,6 +2795,129 @@ void rtw89_phy_cfo_parse(struct rtw89_dev *rtwdev, s16 cfo_val,
cfo->packet_count++;
}
+void rtw89_phy_ul_tb_assoc(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif)
+{
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+ const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_SUB_ENTITY_0);
+ struct rtw89_phy_ul_tb_info *ul_tb_info = &rtwdev->ul_tb_info;
+
+ if (!chip->support_ul_tb_ctrl)
+ return;
+
+ rtwvif->def_tri_idx =
+ rtw89_phy_read32_mask(rtwdev, R_DCFO_OPT, B_TXSHAPE_TRIANGULAR_CFG);
+
+ if (chip->chip_id == RTL8852B && rtwdev->hal.cv > CHIP_CBV)
+ rtwvif->dyn_tb_bedge_en = false;
+ else if (chan->band_type >= RTW89_BAND_5G &&
+ chan->band_width >= RTW89_CHANNEL_WIDTH_40)
+ rtwvif->dyn_tb_bedge_en = true;
+ else
+ rtwvif->dyn_tb_bedge_en = false;
+
+ rtw89_debug(rtwdev, RTW89_DBG_UL_TB,
+ "[ULTB] def_if_bandedge=%d, def_tri_idx=%d\n",
+ ul_tb_info->def_if_bandedge, rtwvif->def_tri_idx);
+ rtw89_debug(rtwdev, RTW89_DBG_UL_TB,
+ "[ULTB] dyn_tb_begde_en=%d, dyn_tb_tri_en=%d\n",
+ rtwvif->dyn_tb_bedge_en, ul_tb_info->dyn_tb_tri_en);
+}
+
+struct rtw89_phy_ul_tb_check_data {
+ bool valid;
+ bool high_tf_client;
+ bool low_tf_client;
+ bool dyn_tb_bedge_en;
+ u8 def_tri_idx;
+};
+
+static
+void rtw89_phy_ul_tb_ctrl_check(struct rtw89_dev *rtwdev,
+ struct rtw89_vif *rtwvif,
+ struct rtw89_phy_ul_tb_check_data *ul_tb_data)
+{
+ struct rtw89_traffic_stats *stats = &rtwdev->stats;
+ struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif);
+
+ if (rtwvif->wifi_role != RTW89_WIFI_ROLE_STATION)
+ return;
+
+ if (!vif->cfg.assoc)
+ return;
+
+ if (stats->rx_tf_periodic > UL_TB_TF_CNT_L2H_TH)
+ ul_tb_data->high_tf_client = true;
+ else if (stats->rx_tf_periodic < UL_TB_TF_CNT_H2L_TH)
+ ul_tb_data->low_tf_client = true;
+
+ ul_tb_data->valid = true;
+ ul_tb_data->def_tri_idx = rtwvif->def_tri_idx;
+ ul_tb_data->dyn_tb_bedge_en = rtwvif->dyn_tb_bedge_en;
+}
+
+void rtw89_phy_ul_tb_ctrl_track(struct rtw89_dev *rtwdev)
+{
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+ struct rtw89_phy_ul_tb_info *ul_tb_info = &rtwdev->ul_tb_info;
+ struct rtw89_phy_ul_tb_check_data ul_tb_data = {};
+ struct rtw89_vif *rtwvif;
+
+ if (!chip->support_ul_tb_ctrl)
+ return;
+
+ if (rtwdev->total_sta_assoc != 1)
+ return;
+
+ rtw89_for_each_rtwvif(rtwdev, rtwvif)
+ rtw89_phy_ul_tb_ctrl_check(rtwdev, rtwvif, &ul_tb_data);
+
+ if (!ul_tb_data.valid)
+ return;
+
+ if (ul_tb_data.dyn_tb_bedge_en) {
+ if (ul_tb_data.high_tf_client) {
+ rtw89_phy_write32_mask(rtwdev, R_BANDEDGE, B_BANDEDGE_EN, 0);
+ rtw89_debug(rtwdev, RTW89_DBG_UL_TB,
+ "[ULTB] Turn off if_bandedge\n");
+ } else if (ul_tb_data.low_tf_client) {
+ rtw89_phy_write32_mask(rtwdev, R_BANDEDGE, B_BANDEDGE_EN,
+ ul_tb_info->def_if_bandedge);
+ rtw89_debug(rtwdev, RTW89_DBG_UL_TB,
+ "[ULTB] Set to default if_bandedge = %d\n",
+ ul_tb_info->def_if_bandedge);
+ }
+ }
+
+ if (ul_tb_info->dyn_tb_tri_en) {
+ if (ul_tb_data.high_tf_client) {
+ rtw89_phy_write32_mask(rtwdev, R_DCFO_OPT,
+ B_TXSHAPE_TRIANGULAR_CFG, 0);
+ rtw89_debug(rtwdev, RTW89_DBG_UL_TB,
+ "[ULTB] Turn off Tx triangle\n");
+ } else if (ul_tb_data.low_tf_client) {
+ rtw89_phy_write32_mask(rtwdev, R_DCFO_OPT,
+ B_TXSHAPE_TRIANGULAR_CFG,
+ ul_tb_data.def_tri_idx);
+ rtw89_debug(rtwdev, RTW89_DBG_UL_TB,
+ "[ULTB] Set to default tx_shap_idx = %d\n",
+ ul_tb_data.def_tri_idx);
+ }
+ }
+}
+
+static void rtw89_phy_ul_tb_info_init(struct rtw89_dev *rtwdev)
+{
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+ struct rtw89_phy_ul_tb_info *ul_tb_info = &rtwdev->ul_tb_info;
+
+ if (!chip->support_ul_tb_ctrl)
+ return;
+
+ ul_tb_info->dyn_tb_tri_en = true;
+ ul_tb_info->def_if_bandedge =
+ rtw89_phy_read32_mask(rtwdev, R_BANDEDGE, B_BANDEDGE_EN);
+}
+
static void rtw89_phy_stat_thermal_update(struct rtw89_dev *rtwdev)
{
struct rtw89_phy_stat *phystat = &rtwdev->phystat;
@@ -3340,7 +3464,7 @@ void rtw89_phy_env_monitor_track(struct rtw89_dev *rtwdev)
static bool rtw89_physts_ie_page_valid(enum rtw89_phy_status_bitmap *ie_page)
{
- if (*ie_page > RTW89_PHYSTS_BITMAP_NUM ||
+ if (*ie_page >= RTW89_PHYSTS_BITMAP_NUM ||
*ie_page == RTW89_RSVD_9)
return false;
else if (*ie_page > RTW89_RSVD_9)
@@ -3980,6 +4104,7 @@ void rtw89_phy_dm_init(struct rtw89_dev *rtwdev)
rtw89_physts_parsing_init(rtwdev);
rtw89_phy_dig_init(rtwdev);
rtw89_phy_cfo_init(rtwdev);
+ rtw89_phy_ul_tb_info_init(rtwdev);
rtw89_phy_init_rf_nctl(rtwdev);
rtw89_chip_rfk_init(rtwdev);
diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h
index dac69a02e868..21233f094644 100644
--- a/drivers/net/wireless/realtek/rtw89/phy.h
+++ b/drivers/net/wireless/realtek/rtw89/phy.h
@@ -64,6 +64,9 @@
#define MAX_CFO_TOLERANCE 30
#define CFO_TF_CNT_TH 300
+#define UL_TB_TF_CNT_L2H_TH 100
+#define UL_TB_TF_CNT_H2L_TH 70
+
#define CCX_MAX_PERIOD 2097
#define CCX_MAX_PERIOD_UNIT 32
#define MS_TO_4US_RATIO 250
@@ -550,5 +553,7 @@ void rtw89_phy_set_bss_color(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif
void rtw89_phy_tssi_ctrl_set_bandedge_cfg(struct rtw89_dev *rtwdev,
enum rtw89_mac_idx mac_idx,
enum rtw89_tssi_bandedge_cfg bandedge_cfg);
+void rtw89_phy_ul_tb_assoc(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif);
+void rtw89_phy_ul_tb_ctrl_track(struct rtw89_dev *rtwdev);
#endif
diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h
index dbe06542f443..f2634062f377 100644
--- a/drivers/net/wireless/realtek/rtw89/reg.h
+++ b/drivers/net/wireless/realtek/rtw89/reg.h
@@ -4236,6 +4236,8 @@
#define R_P1_NBIIDX 0x4770
#define B_P1_NBIIDX_VAL GENMASK(11, 0)
#define B_P1_NBIIDX_NOTCH_EN BIT(12)
+#define R_PKT_CTRL 0x47D4
+#define B_PKT_POP_EN BIT(8)
#define R_SEG0R_PD 0x481C
#define R_SEG0R_PD_V1 0x4860
#define B_SEG0R_PD_SPATIAL_REUSE_EN_MSK_V1 BIT(30)
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c
index 4cea5fb4327d..eff6519cf019 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c
@@ -2082,6 +2082,7 @@ const struct rtw89_chip_info rtw8852a_chip_info = {
.support_bands = BIT(NL80211_BAND_2GHZ) |
BIT(NL80211_BAND_5GHZ),
.support_bw160 = false,
+ .support_ul_tb_ctrl = false,
.hw_sec_hdr = false,
.rf_path_num = 2,
.tx_nss = 2,
@@ -2143,6 +2144,7 @@ const struct rtw89_chip_info rtw8852a_chip_info = {
.c2h_ctrl_reg = R_AX_C2HREG_CTRL,
.c2h_regs = rtw8852a_c2h_regs,
.page_regs = &rtw8852a_page_regs,
+ .cfo_src_fd = false,
.dcfo_comp = &rtw8852a_dcfo_comp,
.dcfo_comp_sft = 3,
.imr_info = &rtw8852a_imr_info,
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c
index 0df044b1c392..b635ac1d1ca2 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c
@@ -318,27 +318,27 @@ static const struct rtw89_dig_regs rtw8852b_dig_regs = {
};
static const struct rtw89_btc_rf_trx_para rtw89_btc_8852b_rf_ul[] = {
- {15, 0, 0, 7}, /* 0 -> original */
- {15, 2, 0, 7}, /* 1 -> for BT-connected ACI issue && BTG co-rx */
- {15, 0, 0, 7}, /* 2 ->reserved for shared-antenna */
- {15, 0, 0, 7}, /* 3- >reserved for shared-antenna */
- {15, 0, 0, 7}, /* 4 ->reserved for shared-antenna */
- {15, 0, 0, 7}, /* the below id is for non-shared-antenna free-run */
+ {255, 0, 0, 7}, /* 0 -> original */
+ {255, 2, 0, 7}, /* 1 -> for BT-connected ACI issue && BTG co-rx */
+ {255, 0, 0, 7}, /* 2 ->reserved for shared-antenna */
+ {255, 0, 0, 7}, /* 3- >reserved for shared-antenna */
+ {255, 0, 0, 7}, /* 4 ->reserved for shared-antenna */
+ {255, 0, 0, 7}, /* the below id is for non-shared-antenna free-run */
{6, 1, 0, 7},
{13, 1, 0, 7},
{13, 1, 0, 7}
};
static const struct rtw89_btc_rf_trx_para rtw89_btc_8852b_rf_dl[] = {
- {15, 0, 0, 7}, /* 0 -> original */
- {15, 2, 0, 7}, /* 1 -> reserved for shared-antenna */
- {15, 0, 0, 7}, /* 2 ->reserved for shared-antenna */
- {15, 0, 0, 7}, /* 3- >reserved for shared-antenna */
- {15, 0, 0, 7}, /* 4 ->reserved for shared-antenna */
- {15, 0, 0, 7}, /* the below id is for non-shared-antenna free-run */
- {15, 1, 0, 7},
- {15, 1, 0, 7},
- {15, 1, 0, 7}
+ {255, 0, 0, 7}, /* 0 -> original */
+ {255, 2, 0, 7}, /* 1 -> reserved for shared-antenna */
+ {255, 0, 0, 7}, /* 2 ->reserved for shared-antenna */
+ {255, 0, 0, 7}, /* 3- >reserved for shared-antenna */
+ {255, 0, 0, 7}, /* 4 ->reserved for shared-antenna */
+ {255, 0, 0, 7}, /* the below id is for non-shared-antenna free-run */
+ {255, 1, 0, 7},
+ {255, 1, 0, 7},
+ {255, 1, 0, 7}
};
static const struct rtw89_btc_fbtc_mreg rtw89_btc_8852b_mon_reg[] = {
@@ -1411,6 +1411,12 @@ static void rtw8852b_bb_sethw(struct rtw89_dev *rtwdev)
rtw89_phy_read32_mask(rtwdev, R_P1_RPL1, B_P0_RPL1_BIAS_MASK);
}
+static void rtw8852b_bb_set_pop(struct rtw89_dev *rtwdev)
+{
+ if (rtwdev->hw->conf.flags & IEEE80211_CONF_MONITOR)
+ rtw89_phy_write32_clr(rtwdev, R_PKT_CTRL, B_PKT_POP_EN);
+}
+
static void rtw8852b_set_channel_bb(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan,
enum rtw89_phy_idx phy_idx)
{
@@ -1441,6 +1447,7 @@ static void rtw8852b_set_channel_bb(struct rtw89_dev *rtwdev, const struct rtw89
rtw89_phy_write32_mask(rtwdev, R_MAC_PIN_SEL, B_CH_IDX_SEG0,
chan->primary_channel);
rtw8852b_5m_mask(rtwdev, chan, phy_idx);
+ rtw8852b_bb_set_pop(rtwdev);
rtw8852b_bb_reset_all(rtwdev, phy_idx);
}
@@ -2452,6 +2459,7 @@ const struct rtw89_chip_info rtw8852b_chip_info = {
.support_bands = BIT(NL80211_BAND_2GHZ) |
BIT(NL80211_BAND_5GHZ),
.support_bw160 = false,
+ .support_ul_tb_ctrl = true,
.hw_sec_hdr = false,
.rf_path_num = 2,
.tx_nss = 2,
@@ -2512,6 +2520,7 @@ const struct rtw89_chip_info rtw8852b_chip_info = {
.c2h_ctrl_reg = R_AX_C2HREG_CTRL,
.c2h_regs = rtw8852b_c2h_regs,
.page_regs = &rtw8852b_page_regs,
+ .cfo_src_fd = true,
.dcfo_comp = &rtw8852b_dcfo_comp,
.dcfo_comp_sft = 3,
.imr_info = &rtw8852b_imr_info,
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c
index 6619ba730719..a87482cc25f5 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c
@@ -1832,11 +1832,11 @@ static void rtw8852c_set_channel_help(struct rtw89_dev *rtwdev, bool enter,
static void rtw8852c_rfk_init(struct rtw89_dev *rtwdev)
{
- struct rtw89_mcc_info *mcc_info = &rtwdev->mcc;
+ struct rtw89_rfk_mcc_info *rfk_mcc = &rtwdev->rfk_mcc;
rtwdev->is_tssi_mode[RF_PATH_A] = false;
rtwdev->is_tssi_mode[RF_PATH_B] = false;
- memset(mcc_info, 0, sizeof(*mcc_info));
+ memset(rfk_mcc, 0, sizeof(*rfk_mcc));
rtw8852c_lck_init(rtwdev);
rtw8852c_rck(rtwdev);
@@ -2891,6 +2891,7 @@ const struct rtw89_chip_info rtw8852c_chip_info = {
BIT(NL80211_BAND_5GHZ) |
BIT(NL80211_BAND_6GHZ),
.support_bw160 = true,
+ .support_ul_tb_ctrl = false,
.hw_sec_hdr = true,
.rf_path_num = 2,
.tx_nss = 2,
@@ -2953,6 +2954,7 @@ const struct rtw89_chip_info rtw8852c_chip_info = {
.c2h_ctrl_reg = R_AX_C2HREG_CTRL_V1,
.c2h_regs = rtw8852c_c2h_regs,
.page_regs = &rtw8852c_page_regs,
+ .cfo_src_fd = false,
.dcfo_comp = &rtw8852c_dcfo_comp,
.dcfo_comp_sft = 5,
.imr_info = &rtw8852c_imr_info,
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8852c_rfk.c
index b0672b906e7b..60cd676fe22c 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852c_rfk.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852c_rfk.c
@@ -1030,9 +1030,9 @@ static bool _iqk_nbtxk(struct rtw89_dev *rtwdev,
static bool _lok_finetune_check(struct rtw89_dev *rtwdev, u8 path)
{
- struct rtw89_mcc_info *mcc_info = &rtwdev->mcc;
+ struct rtw89_rfk_mcc_info *rfk_mcc = &rtwdev->rfk_mcc;
struct rtw89_iqk_info *iqk_info = &rtwdev->iqk;
- u8 idx = mcc_info->table_idx;
+ u8 idx = rfk_mcc->table_idx;
bool is_fail1, is_fail2;
u32 val;
u32 core_i;
@@ -1375,10 +1375,10 @@ static void _iqk_afebb_restore(struct rtw89_dev *rtwdev,
static void _iqk_preset(struct rtw89_dev *rtwdev, u8 path)
{
- struct rtw89_mcc_info *mcc_info = &rtwdev->mcc;
+ struct rtw89_rfk_mcc_info *rfk_mcc = &rtwdev->rfk_mcc;
u8 idx = 0;
- idx = mcc_info->table_idx;
+ idx = rfk_mcc->table_idx;
rtw89_phy_write32_mask(rtwdev, R_COEF_SEL + (path << 8), B_COEF_SEL_IQC, idx);
rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT + (path << 8), B_CFIR_LUT_G3, idx);
rtw89_write_rf(rtwdev, path, RR_RSV1, RR_RSV1_RST, 0x0);
@@ -3824,20 +3824,20 @@ void rtw8852c_set_channel_rf(struct rtw89_dev *rtwdev,
void rtw8852c_mcc_get_ch_info(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx)
{
const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_SUB_ENTITY_0);
- struct rtw89_mcc_info *mcc_info = &rtwdev->mcc;
- u8 idx = mcc_info->table_idx;
+ struct rtw89_rfk_mcc_info *rfk_mcc = &rtwdev->rfk_mcc;
+ u8 idx = rfk_mcc->table_idx;
int i;
for (i = 0; i < RTW89_IQK_CHS_NR; i++) {
- if (mcc_info->ch[idx] == 0)
+ if (rfk_mcc->ch[idx] == 0)
break;
if (++idx >= RTW89_IQK_CHS_NR)
idx = 0;
}
- mcc_info->table_idx = idx;
- mcc_info->ch[idx] = chan->channel;
- mcc_info->band[idx] = chan->band_type;
+ rfk_mcc->table_idx = idx;
+ rfk_mcc->ch[idx] = chan->channel;
+ rfk_mcc->band[idx] = chan->band_type;
}
void rtw8852c_rck(struct rtw89_dev *rtwdev)
diff --git a/drivers/net/wireless/realtek/rtw89/txrx.h b/drivers/net/wireless/realtek/rtw89/txrx.h
index b889e7bf34c0..9d4c6b6fa125 100644
--- a/drivers/net/wireless/realtek/rtw89/txrx.h
+++ b/drivers/net/wireless/realtek/rtw89/txrx.h
@@ -298,7 +298,9 @@
le32_get_bits(*((const __le32 *)ie), GENMASK(11, 5))
#define RTW89_GET_PHY_STS_IE01_CH_IDX(ie) \
le32_get_bits(*((const __le32 *)ie), GENMASK(23, 16))
-#define RTW89_GET_PHY_STS_IE01_CFO(ie) \
+#define RTW89_GET_PHY_STS_IE01_FD_CFO(ie) \
+ le32_get_bits(*((const __le32 *)(ie) + 1), GENMASK(19, 8))
+#define RTW89_GET_PHY_STS_IE01_PREMB_CFO(ie) \
le32_get_bits(*((const __le32 *)(ie) + 1), GENMASK(31, 20))
enum rtw89_tx_channel {
diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c
index 154d58cbd9ce..4bbaccd543ad 100644
--- a/drivers/ptp/ptp_ocp.c
+++ b/drivers/ptp/ptp_ocp.c
@@ -1647,10 +1647,6 @@ ptp_ocp_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req,
char buf[32];
int err;
- err = devlink_info_driver_name_put(req, KBUILD_MODNAME);
- if (err)
- return err;
-
fw_image = bp->fw_loader ? "loader" : "fw";
sprintf(buf, "%d.%d", bp->fw_tag, bp->fw_version);
err = devlink_info_version_running_put(req, fw_image, buf);
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index 6252f02f38b7..80d6308dea06 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -338,6 +338,17 @@ struct ieee80211_qos_hdr {
__le16 qos_ctrl;
} __packed __aligned(2);
+struct ieee80211_qos_hdr_4addr {
+ __le16 frame_control;
+ __le16 duration_id;
+ u8 addr1[ETH_ALEN];
+ u8 addr2[ETH_ALEN];
+ u8 addr3[ETH_ALEN];
+ __le16 seq_ctrl;
+ u8 addr4[ETH_ALEN];
+ __le16 qos_ctrl;
+} __packed __aligned(2);
+
struct ieee80211_trigger {
__le16 frame_control;
__le16 duration;
@@ -4060,16 +4071,21 @@ struct ieee80211_he_6ghz_capa {
* @hdr: the frame
*
* The qos ctrl bytes come after the frame_control, duration, seq_num
- * and 3 or 4 addresses of length ETH_ALEN.
- * 3 addr: 2 + 2 + 2 + 3*6 = 24
- * 4 addr: 2 + 2 + 2 + 4*6 = 30
+ * and 3 or 4 addresses of length ETH_ALEN. Checks frame_control to choose
+ * between struct ieee80211_qos_hdr_4addr and struct ieee80211_qos_hdr.
*/
static inline u8 *ieee80211_get_qos_ctl(struct ieee80211_hdr *hdr)
{
- if (ieee80211_has_a4(hdr->frame_control))
- return (u8 *)hdr + 30;
+ union {
+ struct ieee80211_qos_hdr addr3;
+ struct ieee80211_qos_hdr_4addr addr4;
+ } *qos;
+
+ qos = (void *)hdr;
+ if (ieee80211_has_a4(qos->addr3.frame_control))
+ return (u8 *)&qos->addr4.qos_ctrl;
else
- return (u8 *)hdr + 24;
+ return (u8 *)&qos->addr3.qos_ctrl;
}
/**
diff --git a/include/linux/jump_label.h b/include/linux/jump_label.h
index 570831ca9951..4e968ebadce6 100644
--- a/include/linux/jump_label.h
+++ b/include/linux/jump_label.h
@@ -224,9 +224,10 @@ extern bool arch_jump_label_transform_queue(struct jump_entry *entry,
enum jump_label_type type);
extern void arch_jump_label_transform_apply(void);
extern int jump_label_text_reserved(void *start, void *end);
-extern void static_key_slow_inc(struct static_key *key);
+extern bool static_key_slow_inc(struct static_key *key);
+extern bool static_key_fast_inc_not_disabled(struct static_key *key);
extern void static_key_slow_dec(struct static_key *key);
-extern void static_key_slow_inc_cpuslocked(struct static_key *key);
+extern bool static_key_slow_inc_cpuslocked(struct static_key *key);
extern void static_key_slow_dec_cpuslocked(struct static_key *key);
extern int static_key_count(struct static_key *key);
extern void static_key_enable(struct static_key *key);
@@ -278,11 +279,23 @@ static __always_inline bool static_key_true(struct static_key *key)
return false;
}
-static inline void static_key_slow_inc(struct static_key *key)
+static inline bool static_key_fast_inc_not_disabled(struct static_key *key)
{
+ int v;
+
STATIC_KEY_CHECK_USE(key);
- atomic_inc(&key->enabled);
+ /*
+ * Prevent key->enabled getting negative to follow the same semantics
+ * as for CONFIG_JUMP_LABEL=y, see kernel/jump_label.c comment.
+ */
+ v = atomic_read(&key->enabled);
+ do {
+ if (v < 0 || (v + 1) < 0)
+ return false;
+ } while (!likely(atomic_try_cmpxchg(&key->enabled, &v, v + 1)));
+ return true;
}
+#define static_key_slow_inc(key) static_key_fast_inc_not_disabled(key)
static inline void static_key_slow_dec(struct static_key *key)
{
diff --git a/include/linux/mlx5/device.h b/include/linux/mlx5/device.h
index eb3fac30488b..5fe5d198b57a 100644
--- a/include/linux/mlx5/device.h
+++ b/include/linux/mlx5/device.h
@@ -290,10 +290,9 @@ enum {
MLX5_UMR_INLINE = (1 << 7),
};
-#define MLX5_UMR_KLM_ALIGNMENT 4
-#define MLX5_UMR_MTT_ALIGNMENT 0x40
-#define MLX5_UMR_MTT_MASK (MLX5_UMR_MTT_ALIGNMENT - 1)
-#define MLX5_UMR_MTT_MIN_CHUNK_SIZE MLX5_UMR_MTT_ALIGNMENT
+#define MLX5_UMR_FLEX_ALIGNMENT 0x40
+#define MLX5_UMR_MTT_NUM_ENTRIES_ALIGNMENT (MLX5_UMR_FLEX_ALIGNMENT / sizeof(struct mlx5_mtt))
+#define MLX5_UMR_KLM_NUM_ENTRIES_ALIGNMENT (MLX5_UMR_FLEX_ALIGNMENT / sizeof(struct mlx5_klm))
#define MLX5_USER_INDEX_LEN (MLX5_FLD_SZ_BYTES(qpc, user_index) * 8)
diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h
index 06cbad166225..d476255c9a3f 100644
--- a/include/linux/mlx5/driver.h
+++ b/include/linux/mlx5/driver.h
@@ -606,8 +606,6 @@ struct mlx5_priv {
struct list_head pgdir_list;
/* end: alloc staff */
- struct list_head ctx_list;
- spinlock_t ctx_lock;
struct mlx5_adev **adev;
int adev_idx;
int sw_vhca_id;
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 5aa35c58c342..f78db610ada5 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -78,6 +78,7 @@ struct xdp_buff;
void synchronize_net(void);
void netdev_set_default_ethtool_ops(struct net_device *dev,
const struct ethtool_ops *ops);
+void netdev_sw_irq_coalesce_default_on(struct net_device *dev);
/* Backlog congestion levels */
#define NET_RX_SUCCESS 0 /* keep 'em coming, baby */
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 9a3752c0c444..71eeb4e3b1fd 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -529,6 +529,8 @@ struct macsec_ops;
*
* @mdio: MDIO bus this PHY is on
* @drv: Pointer to the driver for this PHY instance
+ * @devlink: Create a link between phy dev and mac dev, if the external phy
+ * used by current mac interface is managed by another mac interface.
* @phy_id: UID for this device found during discovery
* @c45_ids: 802.3-c45 Device Identifiers if is_c45.
* @is_c45: Set to true if this PHY uses clause 45 addressing.
@@ -618,6 +620,8 @@ struct phy_device {
/* And management functions */
struct phy_driver *drv;
+ struct device_link *devlink;
+
u32 phy_id;
struct phy_c45_device_ids c45_ids;
diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h
index fb2e88614f5d..83ca2e8eb6b5 100644
--- a/include/linux/stmmac.h
+++ b/include/linux/stmmac.h
@@ -271,5 +271,6 @@ struct plat_stmmacenet_data {
int msi_tx_base_vec;
bool use_phy_wol;
bool sph_disable;
+ bool serdes_up_after_phy_linkup;
};
#endif
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 11a370e64143..03d4f4deadae 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -4742,7 +4742,7 @@ struct ieee80211_iface_limit {
*
* struct ieee80211_iface_limit limits1[] = {
* { .max = 1, .types = BIT(NL80211_IFTYPE_STATION), },
- * { .max = 1, .types = BIT(NL80211_IFTYPE_AP}, },
+ * { .max = 1, .types = BIT(NL80211_IFTYPE_AP), },
* };
* struct ieee80211_iface_combination combination1 = {
* .limits = limits1,
diff --git a/include/net/devlink.h b/include/net/devlink.h
index 074a79b8933f..5f6eca5e4a40 100644
--- a/include/net/devlink.h
+++ b/include/net/devlink.h
@@ -650,6 +650,10 @@ struct devlink_info_req;
* the data variable must be updated to point to the snapshot data.
* The function will be called while the devlink instance lock is
* held.
+ * @read: callback to directly read a portion of the region. On success,
+ * the data pointer will be updated with the contents of the
+ * requested portion of the region. The function will be called
+ * while the devlink instance lock is held.
* @priv: Pointer to driver private data for the region operation
*/
struct devlink_region_ops {
@@ -659,6 +663,10 @@ struct devlink_region_ops {
const struct devlink_region_ops *ops,
struct netlink_ext_ack *extack,
u8 **data);
+ int (*read)(struct devlink *devlink,
+ const struct devlink_region_ops *ops,
+ struct netlink_ext_ack *extack,
+ u64 offset, u32 size, u8 *data);
void *priv;
};
@@ -670,6 +678,10 @@ struct devlink_region_ops {
* the data variable must be updated to point to the snapshot data.
* The function will be called while the devlink instance lock is
* held.
+ * @read: callback to directly read a portion of the region. On success,
+ * the data pointer will be updated with the contents of the
+ * requested portion of the region. The function will be called
+ * while the devlink instance lock is held.
* @priv: Pointer to driver private data for the region operation
*/
struct devlink_port_region_ops {
@@ -679,6 +691,10 @@ struct devlink_port_region_ops {
const struct devlink_port_region_ops *ops,
struct netlink_ext_ack *extack,
u8 **data);
+ int (*read)(struct devlink_port *port,
+ const struct devlink_port_region_ops *ops,
+ struct netlink_ext_ack *extack,
+ u64 offset, u32 size, u8 *data);
void *priv;
};
@@ -1746,8 +1762,6 @@ int devlink_region_snapshot_create(struct devlink_region *region,
u8 *data, u32 snapshot_id);
int devlink_info_serial_number_put(struct devlink_info_req *req,
const char *sn);
-int devlink_info_driver_name_put(struct devlink_info_req *req,
- const char *name);
int devlink_info_board_serial_number_put(struct devlink_info_req *req,
const char *bsn);
diff --git a/include/net/fq_impl.h b/include/net/fq_impl.h
index 524b510f1c68..9467e33dfb36 100644
--- a/include/net/fq_impl.h
+++ b/include/net/fq_impl.h
@@ -200,6 +200,7 @@ static void fq_tin_enqueue(struct fq *fq,
fq_skb_free_t free_func)
{
struct fq_flow *flow;
+ struct sk_buff *next;
bool oom;
lockdep_assert_held(&fq->lock);
@@ -214,11 +215,15 @@ static void fq_tin_enqueue(struct fq *fq,
}
flow->tin = tin;
- flow->backlog += skb->len;
- tin->backlog_bytes += skb->len;
- tin->backlog_packets++;
- fq->memory_usage += skb->truesize;
- fq->backlog++;
+ skb_list_walk_safe(skb, skb, next) {
+ skb_mark_not_on_list(skb);
+ flow->backlog += skb->len;
+ tin->backlog_bytes += skb->len;
+ tin->backlog_packets++;
+ fq->memory_usage += skb->truesize;
+ fq->backlog++;
+ __skb_queue_tail(&flow->queue, skb);
+ }
if (list_empty(&flow->flowchain)) {
flow->deficit = fq->quantum;
@@ -226,7 +231,6 @@ static void fq_tin_enqueue(struct fq *fq,
&tin->new_flows);
}
- __skb_queue_tail(&flow->queue, skb);
oom = (fq->memory_usage > fq->memory_limit);
while (fq->backlog > fq->limit || oom) {
flow = fq_find_fattest_flow(fq);
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 721c450a9ccd..689da327ce2e 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -1807,6 +1807,10 @@ struct ieee80211_vif_cfg {
* @addr: address of this interface
* @p2p: indicates whether this AP or STA interface is a p2p
* interface, i.e. a GO or p2p-sta respectively
+ * @netdev_features: tx netdev features supported by the hardware for this
+ * vif. mac80211 initializes this to hw->netdev_features, and the driver
+ * can mask out specific tx features. mac80211 will handle software fixup
+ * for masked offloads (GSO, CSUM)
* @driver_flags: flags/capabilities the driver has for this interface,
* these need to be set (or cleared) when the interface is added
* or, if supported by the driver, the interface type is changed
@@ -1848,6 +1852,7 @@ struct ieee80211_vif {
struct ieee80211_txq *txq;
+ netdev_features_t netdev_features;
u32 driver_flags;
u32 offload_flags;
diff --git a/include/net/sctp/stream_sched.h b/include/net/sctp/stream_sched.h
index 65058faea4db..fa00dc20a0d7 100644
--- a/include/net/sctp/stream_sched.h
+++ b/include/net/sctp/stream_sched.h
@@ -28,8 +28,6 @@ struct sctp_sched_ops {
int (*init_sid)(struct sctp_stream *stream, __u16 sid, gfp_t gfp);
/* free a stream */
void (*free_sid)(struct sctp_stream *stream, __u16 sid);
- /* Frees the entire thing */
- void (*free)(struct sctp_stream *stream);
/* Enqueue a chunk */
void (*enqueue)(struct sctp_outq *q, struct sctp_datamsg *msg);
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 6b814e788f00..f925377066fe 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -1675,7 +1675,11 @@ int tcp_v4_md5_hash_skb(char *md5_hash, const struct tcp_md5sig_key *key,
const struct sock *sk, const struct sk_buff *skb);
int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr,
int family, u8 prefixlen, int l3index, u8 flags,
- const u8 *newkey, u8 newkeylen, gfp_t gfp);
+ const u8 *newkey, u8 newkeylen);
+int tcp_md5_key_copy(struct sock *sk, const union tcp_md5_addr *addr,
+ int family, u8 prefixlen, int l3index,
+ struct tcp_md5sig_key *key);
+
int tcp_md5_do_del(struct sock *sk, const union tcp_md5_addr *addr,
int family, u8 prefixlen, int l3index, u8 flags);
struct tcp_md5sig_key *tcp_v4_md5_lookup(const struct sock *sk,
@@ -1683,7 +1687,7 @@ struct tcp_md5sig_key *tcp_v4_md5_lookup(const struct sock *sk,
#ifdef CONFIG_TCP_MD5SIG
#include <linux/jump_label.h>
-extern struct static_key_false tcp_md5_needed;
+extern struct static_key_false_deferred tcp_md5_needed;
struct tcp_md5sig_key *__tcp_md5_do_lookup(const struct sock *sk, int l3index,
const union tcp_md5_addr *addr,
int family);
@@ -1691,7 +1695,7 @@ static inline struct tcp_md5sig_key *
tcp_md5_do_lookup(const struct sock *sk, int l3index,
const union tcp_md5_addr *addr, int family)
{
- if (!static_branch_unlikely(&tcp_md5_needed))
+ if (!static_branch_unlikely(&tcp_md5_needed.key))
return NULL;
return __tcp_md5_do_lookup(sk, l3index, addr, family);
}
diff --git a/include/uapi/linux/devlink.h b/include/uapi/linux/devlink.h
index 498d0d5d0957..70191d96af89 100644
--- a/include/uapi/linux/devlink.h
+++ b/include/uapi/linux/devlink.h
@@ -610,6 +610,8 @@ enum devlink_attr {
DEVLINK_ATTR_RATE_TX_PRIORITY, /* u32 */
DEVLINK_ATTR_RATE_TX_WEIGHT, /* u32 */
+ DEVLINK_ATTR_REGION_DIRECT, /* flag */
+
/* add new attributes above here, update the policy in devlink.c */
__DEVLINK_ATTR_MAX,
diff --git a/include/uapi/linux/mptcp.h b/include/uapi/linux/mptcp.h
index dfe19bf13f4c..32af2d278cb4 100644
--- a/include/uapi/linux/mptcp.h
+++ b/include/uapi/linux/mptcp.h
@@ -160,6 +160,12 @@ struct mptcp_info {
* daddr4 | daddr6, sport, dport, backup, if_idx
* [, error]
* The priority of a subflow has changed. 'error' should not be set.
+ *
+ * MPTCP_EVENT_LISTENER_CREATED: family, sport, saddr4 | saddr6
+ * A new PM listener is created.
+ *
+ * MPTCP_EVENT_LISTENER_CLOSED: family, sport, saddr4 | saddr6
+ * A PM listener is closed.
*/
enum mptcp_event_type {
MPTCP_EVENT_UNSPEC = 0,
@@ -174,6 +180,9 @@ enum mptcp_event_type {
MPTCP_EVENT_SUB_CLOSED = 11,
MPTCP_EVENT_SUB_PRIORITY = 13,
+
+ MPTCP_EVENT_LISTENER_CREATED = 15,
+ MPTCP_EVENT_LISTENER_CLOSED = 16,
};
enum mptcp_event_attr {
diff --git a/kernel/jump_label.c b/kernel/jump_label.c
index 714ac4c3b556..d9c822bbffb8 100644
--- a/kernel/jump_label.c
+++ b/kernel/jump_label.c
@@ -113,11 +113,40 @@ int static_key_count(struct static_key *key)
}
EXPORT_SYMBOL_GPL(static_key_count);
-void static_key_slow_inc_cpuslocked(struct static_key *key)
+/*
+ * static_key_fast_inc_not_disabled - adds a user for a static key
+ * @key: static key that must be already enabled
+ *
+ * The caller must make sure that the static key can't get disabled while
+ * in this function. It doesn't patch jump labels, only adds a user to
+ * an already enabled static key.
+ *
+ * Returns true if the increment was done. Unlike refcount_t the ref counter
+ * is not saturated, but will fail to increment on overflow.
+ */
+bool static_key_fast_inc_not_disabled(struct static_key *key)
{
- int v, v1;
+ int v;
STATIC_KEY_CHECK_USE(key);
+ /*
+ * Negative key->enabled has a special meaning: it sends
+ * static_key_slow_inc() down the slow path, and it is non-zero
+ * so it counts as "enabled" in jump_label_update(). Note that
+ * atomic_inc_unless_negative() checks >= 0, so roll our own.
+ */
+ v = atomic_read(&key->enabled);
+ do {
+ if (v <= 0 || (v + 1) < 0)
+ return false;
+ } while (!likely(atomic_try_cmpxchg(&key->enabled, &v, v + 1)));
+
+ return true;
+}
+EXPORT_SYMBOL_GPL(static_key_fast_inc_not_disabled);
+
+bool static_key_slow_inc_cpuslocked(struct static_key *key)
+{
lockdep_assert_cpus_held();
/*
@@ -126,17 +155,9 @@ void static_key_slow_inc_cpuslocked(struct static_key *key)
* jump_label_update() process. At the same time, however,
* the jump_label_update() call below wants to see
* static_key_enabled(&key) for jumps to be updated properly.
- *
- * So give a special meaning to negative key->enabled: it sends
- * static_key_slow_inc() down the slow path, and it is non-zero
- * so it counts as "enabled" in jump_label_update(). Note that
- * atomic_inc_unless_negative() checks >= 0, so roll our own.
*/
- for (v = atomic_read(&key->enabled); v > 0; v = v1) {
- v1 = atomic_cmpxchg(&key->enabled, v, v + 1);
- if (likely(v1 == v))
- return;
- }
+ if (static_key_fast_inc_not_disabled(key))
+ return true;
jump_label_lock();
if (atomic_read(&key->enabled) == 0) {
@@ -148,16 +169,23 @@ void static_key_slow_inc_cpuslocked(struct static_key *key)
*/
atomic_set_release(&key->enabled, 1);
} else {
- atomic_inc(&key->enabled);
+ if (WARN_ON_ONCE(!static_key_fast_inc_not_disabled(key))) {
+ jump_label_unlock();
+ return false;
+ }
}
jump_label_unlock();
+ return true;
}
-void static_key_slow_inc(struct static_key *key)
+bool static_key_slow_inc(struct static_key *key)
{
+ bool ret;
+
cpus_read_lock();
- static_key_slow_inc_cpuslocked(key);
+ ret = static_key_slow_inc_cpuslocked(key);
cpus_read_unlock();
+ return ret;
}
EXPORT_SYMBOL_GPL(static_key_slow_inc);
diff --git a/net/core/dev.c b/net/core/dev.c
index 7627c475d991..b76fb37b381e 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -10517,6 +10517,22 @@ void netdev_set_default_ethtool_ops(struct net_device *dev,
}
EXPORT_SYMBOL_GPL(netdev_set_default_ethtool_ops);
+/**
+ * netdev_sw_irq_coalesce_default_on() - enable SW IRQ coalescing by default
+ * @dev: netdev to enable the IRQ coalescing on
+ *
+ * Sets a conservative default for SW IRQ coalescing. Users can use
+ * sysfs attributes to override the default values.
+ */
+void netdev_sw_irq_coalesce_default_on(struct net_device *dev)
+{
+ WARN_ON(dev->reg_state == NETREG_REGISTERED);
+
+ dev->gro_flush_timeout = 20000;
+ dev->napi_defer_hard_irqs = 1;
+}
+EXPORT_SYMBOL_GPL(netdev_sw_irq_coalesce_default_on);
+
void netdev_freemem(struct net_device *dev)
{
char *addr = (char *)dev - dev->padded;
diff --git a/net/core/devlink.c b/net/core/devlink.c
index 0e10a8a68c5e..907df7124157 100644
--- a/net/core/devlink.c
+++ b/net/core/devlink.c
@@ -41,7 +41,7 @@ struct devlink_dev_stats {
struct devlink {
u32 index;
- struct list_head port_list;
+ struct xarray ports;
struct list_head rate_list;
struct list_head sb_list;
struct list_head dpipe_table_list;
@@ -382,19 +382,7 @@ static struct devlink *devlink_get_from_attrs(struct net *net,
static struct devlink_port *devlink_port_get_by_index(struct devlink *devlink,
unsigned int port_index)
{
- struct devlink_port *devlink_port;
-
- list_for_each_entry(devlink_port, &devlink->port_list, list) {
- if (devlink_port->index == port_index)
- return devlink_port;
- }
- return NULL;
-}
-
-static bool devlink_port_index_exists(struct devlink *devlink,
- unsigned int port_index)
-{
- return devlink_port_get_by_index(devlink, port_index);
+ return xa_load(&devlink->ports, port_index);
}
static struct devlink_port *devlink_port_get_from_attrs(struct devlink *devlink,
@@ -1565,14 +1553,14 @@ static int devlink_nl_cmd_port_get_dumpit(struct sk_buff *msg,
{
struct devlink *devlink;
struct devlink_port *devlink_port;
+ unsigned long index, port_index;
int start = cb->args[0];
- unsigned long index;
int idx = 0;
int err;
devlinks_xa_for_each_registered_get(sock_net(msg->sk), index, devlink) {
devl_lock(devlink);
- list_for_each_entry(devlink_port, &devlink->port_list, list) {
+ xa_for_each(&devlink->ports, port_index, devlink_port) {
if (idx < start) {
idx++;
continue;
@@ -2886,10 +2874,11 @@ static int __sb_port_pool_get_dumpit(struct sk_buff *msg, int start, int *p_idx,
{
struct devlink_port *devlink_port;
u16 pool_count = devlink_sb_pool_count(devlink_sb);
+ unsigned long port_index;
u16 pool_index;
int err;
- list_for_each_entry(devlink_port, &devlink->port_list, list) {
+ xa_for_each(&devlink->ports, port_index, devlink_port) {
for (pool_index = 0; pool_index < pool_count; pool_index++) {
if (*p_idx < start) {
(*p_idx)++;
@@ -3107,10 +3096,11 @@ static int __sb_tc_pool_bind_get_dumpit(struct sk_buff *msg,
u32 portid, u32 seq)
{
struct devlink_port *devlink_port;
+ unsigned long port_index;
u16 tc_index;
int err;
- list_for_each_entry(devlink_port, &devlink->port_list, list) {
+ xa_for_each(&devlink->ports, port_index, devlink_port) {
for (tc_index = 0;
tc_index < devlink_sb->ingress_tc_count; tc_index++) {
if (*p_idx < start) {
@@ -6207,6 +6197,7 @@ static int devlink_nl_cmd_region_get_devlink_dumpit(struct sk_buff *msg,
{
struct devlink_region *region;
struct devlink_port *port;
+ unsigned long port_index;
int err = 0;
devl_lock(devlink);
@@ -6225,7 +6216,7 @@ static int devlink_nl_cmd_region_get_devlink_dumpit(struct sk_buff *msg,
(*idx)++;
}
- list_for_each_entry(port, &devlink->port_list, list) {
+ xa_for_each(&devlink->ports, port_index, port) {
err = devlink_nl_cmd_region_get_port_dumpit(msg, cb, port, idx,
start);
if (err)
@@ -6431,7 +6422,6 @@ unlock:
}
static int devlink_nl_cmd_region_read_chunk_fill(struct sk_buff *msg,
- struct devlink *devlink,
u8 *chunk, u32 chunk_size,
u64 addr)
{
@@ -6461,39 +6451,37 @@ nla_put_failure:
#define DEVLINK_REGION_READ_CHUNK_SIZE 256
-static int devlink_nl_region_read_snapshot_fill(struct sk_buff *skb,
- struct devlink *devlink,
- struct devlink_region *region,
- struct nlattr **attrs,
- u64 start_offset,
- u64 end_offset,
- u64 *new_offset)
+typedef int devlink_chunk_fill_t(void *cb_priv, u8 *chunk, u32 chunk_size,
+ u64 curr_offset,
+ struct netlink_ext_ack *extack);
+
+static int
+devlink_nl_region_read_fill(struct sk_buff *skb, devlink_chunk_fill_t *cb,
+ void *cb_priv, u64 start_offset, u64 end_offset,
+ u64 *new_offset, struct netlink_ext_ack *extack)
{
- struct devlink_snapshot *snapshot;
u64 curr_offset = start_offset;
- u32 snapshot_id;
int err = 0;
+ u8 *data;
- *new_offset = start_offset;
+ /* Allocate and re-use a single buffer */
+ data = kmalloc(DEVLINK_REGION_READ_CHUNK_SIZE, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
- snapshot_id = nla_get_u32(attrs[DEVLINK_ATTR_REGION_SNAPSHOT_ID]);
- snapshot = devlink_region_snapshot_get_by_id(region, snapshot_id);
- if (!snapshot)
- return -EINVAL;
+ *new_offset = start_offset;
while (curr_offset < end_offset) {
u32 data_size;
- u8 *data;
- if (end_offset - curr_offset < DEVLINK_REGION_READ_CHUNK_SIZE)
- data_size = end_offset - curr_offset;
- else
- data_size = DEVLINK_REGION_READ_CHUNK_SIZE;
+ data_size = min_t(u32, end_offset - curr_offset,
+ DEVLINK_REGION_READ_CHUNK_SIZE);
+
+ err = cb(cb_priv, data, data_size, curr_offset, extack);
+ if (err)
+ break;
- data = &snapshot->data[curr_offset];
- err = devlink_nl_cmd_region_read_chunk_fill(skb, devlink,
- data, data_size,
- curr_offset);
+ err = devlink_nl_cmd_region_read_chunk_fill(skb, data, data_size, curr_offset);
if (err)
break;
@@ -6501,21 +6489,57 @@ static int devlink_nl_region_read_snapshot_fill(struct sk_buff *skb,
}
*new_offset = curr_offset;
+ kfree(data);
+
return err;
}
+static int
+devlink_region_snapshot_fill(void *cb_priv, u8 *chunk, u32 chunk_size,
+ u64 curr_offset,
+ struct netlink_ext_ack __always_unused *extack)
+{
+ struct devlink_snapshot *snapshot = cb_priv;
+
+ memcpy(chunk, &snapshot->data[curr_offset], chunk_size);
+
+ return 0;
+}
+
+static int
+devlink_region_port_direct_fill(void *cb_priv, u8 *chunk, u32 chunk_size,
+ u64 curr_offset, struct netlink_ext_ack *extack)
+{
+ struct devlink_region *region = cb_priv;
+
+ return region->port_ops->read(region->port, region->port_ops, extack,
+ curr_offset, chunk_size, chunk);
+}
+
+static int
+devlink_region_direct_fill(void *cb_priv, u8 *chunk, u32 chunk_size,
+ u64 curr_offset, struct netlink_ext_ack *extack)
+{
+ struct devlink_region *region = cb_priv;
+
+ return region->ops->read(region->devlink, region->ops, extack,
+ curr_offset, chunk_size, chunk);
+}
+
static int devlink_nl_cmd_region_read_dumpit(struct sk_buff *skb,
struct netlink_callback *cb)
{
const struct genl_dumpit_info *info = genl_dumpit_info(cb);
+ struct nlattr *chunks_attr, *region_attr, *snapshot_attr;
u64 ret_offset, start_offset, end_offset = U64_MAX;
struct nlattr **attrs = info->attrs;
struct devlink_port *port = NULL;
+ devlink_chunk_fill_t *region_cb;
struct devlink_region *region;
- struct nlattr *chunks_attr;
const char *region_name;
struct devlink *devlink;
unsigned int index;
+ void *region_cb_priv;
void *hdr;
int err;
@@ -6527,8 +6551,8 @@ static int devlink_nl_cmd_region_read_dumpit(struct sk_buff *skb,
devl_lock(devlink);
- if (!attrs[DEVLINK_ATTR_REGION_NAME] ||
- !attrs[DEVLINK_ATTR_REGION_SNAPSHOT_ID]) {
+ if (!attrs[DEVLINK_ATTR_REGION_NAME]) {
+ NL_SET_ERR_MSG(cb->extack, "No region name provided");
err = -EINVAL;
goto out_unlock;
}
@@ -6543,7 +6567,8 @@ static int devlink_nl_cmd_region_read_dumpit(struct sk_buff *skb,
}
}
- region_name = nla_data(attrs[DEVLINK_ATTR_REGION_NAME]);
+ region_attr = attrs[DEVLINK_ATTR_REGION_NAME];
+ region_name = nla_data(region_attr);
if (port)
region = devlink_port_region_get_by_name(port, region_name);
@@ -6551,10 +6576,51 @@ static int devlink_nl_cmd_region_read_dumpit(struct sk_buff *skb,
region = devlink_region_get_by_name(devlink, region_name);
if (!region) {
+ NL_SET_ERR_MSG_ATTR(cb->extack, region_attr, "Requested region does not exist");
err = -EINVAL;
goto out_unlock;
}
+ snapshot_attr = attrs[DEVLINK_ATTR_REGION_SNAPSHOT_ID];
+ if (!snapshot_attr) {
+ if (!nla_get_flag(attrs[DEVLINK_ATTR_REGION_DIRECT])) {
+ NL_SET_ERR_MSG(cb->extack, "No snapshot id provided");
+ err = -EINVAL;
+ goto out_unlock;
+ }
+
+ if (!region->ops->read) {
+ NL_SET_ERR_MSG(cb->extack, "Requested region does not support direct read");
+ err = -EOPNOTSUPP;
+ goto out_unlock;
+ }
+
+ if (port)
+ region_cb = &devlink_region_port_direct_fill;
+ else
+ region_cb = &devlink_region_direct_fill;
+ region_cb_priv = region;
+ } else {
+ struct devlink_snapshot *snapshot;
+ u32 snapshot_id;
+
+ if (nla_get_flag(attrs[DEVLINK_ATTR_REGION_DIRECT])) {
+ NL_SET_ERR_MSG_ATTR(cb->extack, snapshot_attr, "Direct region read does not use snapshot");
+ err = -EINVAL;
+ goto out_unlock;
+ }
+
+ snapshot_id = nla_get_u32(snapshot_attr);
+ snapshot = devlink_region_snapshot_get_by_id(region, snapshot_id);
+ if (!snapshot) {
+ NL_SET_ERR_MSG_ATTR(cb->extack, snapshot_attr, "Requested snapshot does not exist");
+ err = -EINVAL;
+ goto out_unlock;
+ }
+ region_cb = &devlink_region_snapshot_fill;
+ region_cb_priv = snapshot;
+ }
+
if (attrs[DEVLINK_ATTR_REGION_CHUNK_ADDR] &&
attrs[DEVLINK_ATTR_REGION_CHUNK_LEN]) {
if (!start_offset)
@@ -6603,10 +6669,9 @@ static int devlink_nl_cmd_region_read_dumpit(struct sk_buff *skb,
goto nla_put_failure;
}
- err = devlink_nl_region_read_snapshot_fill(skb, devlink,
- region, attrs,
- start_offset,
- end_offset, &ret_offset);
+ err = devlink_nl_region_read_fill(skb, region_cb, region_cb_priv,
+ start_offset, end_offset, &ret_offset,
+ cb->extack);
if (err && err != -EMSGSIZE)
goto nla_put_failure;
@@ -6633,14 +6698,6 @@ out_unlock:
return err;
}
-int devlink_info_driver_name_put(struct devlink_info_req *req, const char *name)
-{
- if (!req->msg)
- return 0;
- return nla_put_string(req->msg, DEVLINK_ATTR_INFO_DRIVER_NAME, name);
-}
-EXPORT_SYMBOL_GPL(devlink_info_driver_name_put);
-
int devlink_info_serial_number_put(struct devlink_info_req *req, const char *sn)
{
if (!req->msg)
@@ -6749,11 +6806,25 @@ int devlink_info_version_running_put_ext(struct devlink_info_req *req,
}
EXPORT_SYMBOL_GPL(devlink_info_version_running_put_ext);
+static int devlink_nl_driver_info_get(struct device_driver *drv,
+ struct devlink_info_req *req)
+{
+ if (!drv)
+ return 0;
+
+ if (drv->name[0])
+ return nla_put_string(req->msg, DEVLINK_ATTR_INFO_DRIVER_NAME,
+ drv->name);
+
+ return 0;
+}
+
static int
devlink_nl_info_fill(struct sk_buff *msg, struct devlink *devlink,
enum devlink_command cmd, u32 portid,
u32 seq, int flags, struct netlink_ext_ack *extack)
{
+ struct device *dev = devlink_to_dev(devlink);
struct devlink_info_req req = {};
void *hdr;
int err;
@@ -6767,7 +6838,13 @@ devlink_nl_info_fill(struct sk_buff *msg, struct devlink *devlink,
goto err_cancel_msg;
req.msg = msg;
- err = devlink->ops->info_get(devlink, &req, extack);
+ if (devlink->ops->info_get) {
+ err = devlink->ops->info_get(devlink, &req, extack);
+ if (err)
+ goto err_cancel_msg;
+ }
+
+ err = devlink_nl_driver_info_get(dev->driver, &req);
if (err)
goto err_cancel_msg;
@@ -6786,9 +6863,6 @@ static int devlink_nl_cmd_info_get_doit(struct sk_buff *skb,
struct sk_buff *msg;
int err;
- if (!devlink->ops->info_get)
- return -EOPNOTSUPP;
-
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!msg)
return -ENOMEM;
@@ -6814,7 +6888,7 @@ static int devlink_nl_cmd_info_get_dumpit(struct sk_buff *msg,
int err = 0;
devlinks_xa_for_each_registered_get(sock_net(msg->sk), index, devlink) {
- if (idx < start || !devlink->ops->info_get)
+ if (idx < start)
goto inc;
devl_lock(devlink);
@@ -7974,10 +8048,10 @@ devlink_nl_cmd_health_reporter_get_dumpit(struct sk_buff *msg,
struct netlink_callback *cb)
{
struct devlink_health_reporter *reporter;
+ unsigned long index, port_index;
struct devlink_port *port;
struct devlink *devlink;
int start = cb->args[0];
- unsigned long index;
int idx = 0;
int err;
@@ -8006,7 +8080,7 @@ devlink_nl_cmd_health_reporter_get_dumpit(struct sk_buff *msg,
devlinks_xa_for_each_registered_get(sock_net(msg->sk), index, devlink) {
devl_lock(devlink);
- list_for_each_entry(port, &devlink->port_list, list) {
+ xa_for_each(&devlink->ports, port_index, port) {
mutex_lock(&port->reporters_lock);
list_for_each_entry(reporter, &port->reporter_list, list) {
if (idx < start) {
@@ -9251,6 +9325,7 @@ static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = {
[DEVLINK_ATTR_SELFTESTS] = { .type = NLA_NESTED },
[DEVLINK_ATTR_RATE_TX_PRIORITY] = { .type = NLA_U32 },
[DEVLINK_ATTR_RATE_TX_WEIGHT] = { .type = NLA_U32 },
+ [DEVLINK_ATTR_REGION_DIRECT] = { .type = NLA_FLAG },
};
static const struct genl_small_ops devlink_nl_ops[] = {
@@ -9724,9 +9799,9 @@ struct devlink *devlink_alloc_ns(const struct devlink_ops *ops,
devlink->dev = dev;
devlink->ops = ops;
+ xa_init_flags(&devlink->ports, XA_FLAGS_ALLOC);
xa_init_flags(&devlink->snapshot_ids, XA_FLAGS_ALLOC);
write_pnet(&devlink->_net, net);
- INIT_LIST_HEAD(&devlink->port_list);
INIT_LIST_HEAD(&devlink->rate_list);
INIT_LIST_HEAD(&devlink->linecard_list);
INIT_LIST_HEAD(&devlink->sb_list);
@@ -9778,12 +9853,13 @@ static void devlink_notify_register(struct devlink *devlink)
struct devlink_linecard *linecard;
struct devlink_rate *rate_node;
struct devlink_region *region;
+ unsigned long port_index;
devlink_notify(devlink, DEVLINK_CMD_NEW);
list_for_each_entry(linecard, &devlink->linecard_list, list)
devlink_linecard_notify(linecard, DEVLINK_CMD_LINECARD_NEW);
- list_for_each_entry(devlink_port, &devlink->port_list, list)
+ xa_for_each(&devlink->ports, port_index, devlink_port)
devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_NEW);
list_for_each_entry(policer_item, &devlink->trap_policer_list, list)
@@ -9817,6 +9893,7 @@ static void devlink_notify_unregister(struct devlink *devlink)
struct devlink_port *devlink_port;
struct devlink_rate *rate_node;
struct devlink_region *region;
+ unsigned long port_index;
list_for_each_entry_reverse(param_item, &devlink->param_list, list)
devlink_param_notify(devlink, 0, param_item,
@@ -9839,7 +9916,7 @@ static void devlink_notify_unregister(struct devlink *devlink)
devlink_trap_policer_notify(devlink, policer_item,
DEVLINK_CMD_TRAP_POLICER_DEL);
- list_for_each_entry_reverse(devlink_port, &devlink->port_list, list)
+ xa_for_each(&devlink->ports, port_index, devlink_port)
devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_DEL);
devlink_notify(devlink, DEVLINK_CMD_DEL);
}
@@ -9903,9 +9980,10 @@ void devlink_free(struct devlink *devlink)
WARN_ON(!list_empty(&devlink->sb_list));
WARN_ON(!list_empty(&devlink->rate_list));
WARN_ON(!list_empty(&devlink->linecard_list));
- WARN_ON(!list_empty(&devlink->port_list));
+ WARN_ON(!xa_empty(&devlink->ports));
xa_destroy(&devlink->snapshot_ids);
+ xa_destroy(&devlink->ports);
WARN_ON_ONCE(unregister_netdevice_notifier_net(devlink_net(devlink),
&devlink->netdevice_nb));
@@ -10004,10 +10082,9 @@ int devl_port_register(struct devlink *devlink,
struct devlink_port *devlink_port,
unsigned int port_index)
{
- devl_assert_locked(devlink);
+ int err;
- if (devlink_port_index_exists(devlink, port_index))
- return -EEXIST;
+ devl_assert_locked(devlink);
ASSERT_DEVLINK_PORT_NOT_REGISTERED(devlink_port);
@@ -10017,7 +10094,11 @@ int devl_port_register(struct devlink *devlink,
spin_lock_init(&devlink_port->type_lock);
INIT_LIST_HEAD(&devlink_port->reporter_list);
mutex_init(&devlink_port->reporters_lock);
- list_add_tail(&devlink_port->list, &devlink->port_list);
+ err = xa_insert(&devlink->ports, port_index, devlink_port, GFP_KERNEL);
+ if (err) {
+ mutex_destroy(&devlink_port->reporters_lock);
+ return err;
+ }
INIT_DELAYED_WORK(&devlink_port->type_warn_dw, &devlink_port_type_warn);
devlink_port_type_warn_schedule(devlink_port);
@@ -10066,7 +10147,7 @@ void devl_port_unregister(struct devlink_port *devlink_port)
devlink_port_type_warn_cancel(devlink_port);
devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_DEL);
- list_del(&devlink_port->list);
+ xa_erase(&devlink_port->devlink->ports, devlink_port->index);
WARN_ON(!list_empty(&devlink_port->reporter_list));
mutex_destroy(&devlink_port->reporters_lock);
devlink_port->registered = false;
diff --git a/net/hsr/hsr_debugfs.c b/net/hsr/hsr_debugfs.c
index de476a417631..1a195efc79cd 100644
--- a/net/hsr/hsr_debugfs.c
+++ b/net/hsr/hsr_debugfs.c
@@ -9,7 +9,6 @@
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/debugfs.h>
-#include <linux/jhash.h>
#include "hsr_main.h"
#include "hsr_framereg.h"
@@ -21,7 +20,6 @@ hsr_node_table_show(struct seq_file *sfp, void *data)
{
struct hsr_priv *priv = (struct hsr_priv *)sfp->private;
struct hsr_node *node;
- int i;
seq_printf(sfp, "Node Table entries for (%s) device\n",
(priv->prot_version == PRP_V1 ? "PRP" : "HSR"));
@@ -33,28 +31,22 @@ hsr_node_table_show(struct seq_file *sfp, void *data)
seq_puts(sfp, "DAN-H\n");
rcu_read_lock();
-
- for (i = 0 ; i < priv->hash_buckets; i++) {
- hlist_for_each_entry_rcu(node, &priv->node_db[i], mac_list) {
- /* skip self node */
- if (hsr_addr_is_self(priv, node->macaddress_A))
- continue;
- seq_printf(sfp, "%pM ", &node->macaddress_A[0]);
- seq_printf(sfp, "%pM ", &node->macaddress_B[0]);
- seq_printf(sfp, "%10lx, ",
- node->time_in[HSR_PT_SLAVE_A]);
- seq_printf(sfp, "%10lx, ",
- node->time_in[HSR_PT_SLAVE_B]);
- seq_printf(sfp, "%14x, ", node->addr_B_port);
-
- if (priv->prot_version == PRP_V1)
- seq_printf(sfp, "%5x, %5x, %5x\n",
- node->san_a, node->san_b,
- (node->san_a == 0 &&
- node->san_b == 0));
- else
- seq_printf(sfp, "%5x\n", 1);
- }
+ list_for_each_entry_rcu(node, &priv->node_db, mac_list) {
+ /* skip self node */
+ if (hsr_addr_is_self(priv, node->macaddress_A))
+ continue;
+ seq_printf(sfp, "%pM ", &node->macaddress_A[0]);
+ seq_printf(sfp, "%pM ", &node->macaddress_B[0]);
+ seq_printf(sfp, "%10lx, ", node->time_in[HSR_PT_SLAVE_A]);
+ seq_printf(sfp, "%10lx, ", node->time_in[HSR_PT_SLAVE_B]);
+ seq_printf(sfp, "%14x, ", node->addr_B_port);
+
+ if (priv->prot_version == PRP_V1)
+ seq_printf(sfp, "%5x, %5x, %5x\n",
+ node->san_a, node->san_b,
+ (node->san_a == 0 && node->san_b == 0));
+ else
+ seq_printf(sfp, "%5x\n", 1);
}
rcu_read_unlock();
return 0;
diff --git a/net/hsr/hsr_device.c b/net/hsr/hsr_device.c
index 6ffef47e9be5..5a236aae2366 100644
--- a/net/hsr/hsr_device.c
+++ b/net/hsr/hsr_device.c
@@ -219,7 +219,9 @@ static netdev_tx_t hsr_dev_xmit(struct sk_buff *skb, struct net_device *dev)
skb->dev = master->dev;
skb_reset_mac_header(skb);
skb_reset_mac_len(skb);
+ spin_lock_bh(&hsr->seqnr_lock);
hsr_forward_skb(skb, master);
+ spin_unlock_bh(&hsr->seqnr_lock);
} else {
dev_core_stats_tx_dropped_inc(dev);
dev_kfree_skb_any(skb);
@@ -278,7 +280,6 @@ static void send_hsr_supervision_frame(struct hsr_port *master,
__u8 type = HSR_TLV_LIFE_CHECK;
struct hsr_sup_payload *hsr_sp;
struct hsr_sup_tag *hsr_stag;
- unsigned long irqflags;
struct sk_buff *skb;
*interval = msecs_to_jiffies(HSR_LIFE_CHECK_INTERVAL);
@@ -299,7 +300,7 @@ static void send_hsr_supervision_frame(struct hsr_port *master,
set_hsr_stag_HSR_ver(hsr_stag, hsr->prot_version);
/* From HSRv1 on we have separate supervision sequence numbers. */
- spin_lock_irqsave(&master->hsr->seqnr_lock, irqflags);
+ spin_lock_bh(&hsr->seqnr_lock);
if (hsr->prot_version > 0) {
hsr_stag->sequence_nr = htons(hsr->sup_sequence_nr);
hsr->sup_sequence_nr++;
@@ -307,7 +308,6 @@ static void send_hsr_supervision_frame(struct hsr_port *master,
hsr_stag->sequence_nr = htons(hsr->sequence_nr);
hsr->sequence_nr++;
}
- spin_unlock_irqrestore(&master->hsr->seqnr_lock, irqflags);
hsr_stag->tlv.HSR_TLV_type = type;
/* TODO: Why 12 in HSRv0? */
@@ -318,11 +318,13 @@ static void send_hsr_supervision_frame(struct hsr_port *master,
hsr_sp = skb_put(skb, sizeof(struct hsr_sup_payload));
ether_addr_copy(hsr_sp->macaddress_A, master->dev->dev_addr);
- if (skb_put_padto(skb, ETH_ZLEN))
+ if (skb_put_padto(skb, ETH_ZLEN)) {
+ spin_unlock_bh(&hsr->seqnr_lock);
return;
+ }
hsr_forward_skb(skb, master);
-
+ spin_unlock_bh(&hsr->seqnr_lock);
return;
}
@@ -332,7 +334,6 @@ static void send_prp_supervision_frame(struct hsr_port *master,
struct hsr_priv *hsr = master->hsr;
struct hsr_sup_payload *hsr_sp;
struct hsr_sup_tag *hsr_stag;
- unsigned long irqflags;
struct sk_buff *skb;
skb = hsr_init_skb(master);
@@ -347,7 +348,7 @@ static void send_prp_supervision_frame(struct hsr_port *master,
set_hsr_stag_HSR_ver(hsr_stag, (hsr->prot_version ? 1 : 0));
/* From HSRv1 on we have separate supervision sequence numbers. */
- spin_lock_irqsave(&master->hsr->seqnr_lock, irqflags);
+ spin_lock_bh(&hsr->seqnr_lock);
hsr_stag->sequence_nr = htons(hsr->sup_sequence_nr);
hsr->sup_sequence_nr++;
hsr_stag->tlv.HSR_TLV_type = PRP_TLV_LIFE_CHECK_DD;
@@ -358,13 +359,12 @@ static void send_prp_supervision_frame(struct hsr_port *master,
ether_addr_copy(hsr_sp->macaddress_A, master->dev->dev_addr);
if (skb_put_padto(skb, ETH_ZLEN)) {
- spin_unlock_irqrestore(&master->hsr->seqnr_lock, irqflags);
+ spin_unlock_bh(&hsr->seqnr_lock);
return;
}
- spin_unlock_irqrestore(&master->hsr->seqnr_lock, irqflags);
-
hsr_forward_skb(skb, master);
+ spin_unlock_bh(&hsr->seqnr_lock);
}
/* Announce (supervision frame) timer function
@@ -444,7 +444,7 @@ void hsr_dev_setup(struct net_device *dev)
dev->header_ops = &hsr_header_ops;
dev->netdev_ops = &hsr_device_ops;
SET_NETDEV_DEVTYPE(dev, &hsr_type);
- dev->priv_flags |= IFF_NO_QUEUE;
+ dev->priv_flags |= IFF_NO_QUEUE | IFF_DISABLE_NETPOLL;
dev->needs_free_netdev = true;
@@ -485,16 +485,11 @@ int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2],
{
bool unregister = false;
struct hsr_priv *hsr;
- int res, i;
+ int res;
hsr = netdev_priv(hsr_dev);
INIT_LIST_HEAD(&hsr->ports);
- INIT_HLIST_HEAD(&hsr->self_node_db);
- hsr->hash_buckets = HSR_HSIZE;
- get_random_bytes(&hsr->hash_seed, sizeof(hsr->hash_seed));
- for (i = 0; i < hsr->hash_buckets; i++)
- INIT_HLIST_HEAD(&hsr->node_db[i]);
-
+ INIT_LIST_HEAD(&hsr->node_db);
spin_lock_init(&hsr->list_lock);
eth_hw_addr_set(hsr_dev, slave[0]->dev_addr);
diff --git a/net/hsr/hsr_forward.c b/net/hsr/hsr_forward.c
index 56bb27d67a2e..629daacc9607 100644
--- a/net/hsr/hsr_forward.c
+++ b/net/hsr/hsr_forward.c
@@ -500,7 +500,6 @@ static void handle_std_frame(struct sk_buff *skb,
{
struct hsr_port *port = frame->port_rcv;
struct hsr_priv *hsr = port->hsr;
- unsigned long irqflags;
frame->skb_hsr = NULL;
frame->skb_prp = NULL;
@@ -510,10 +509,9 @@ static void handle_std_frame(struct sk_buff *skb,
frame->is_from_san = true;
} else {
/* Sequence nr for the master node */
- spin_lock_irqsave(&hsr->seqnr_lock, irqflags);
+ lockdep_assert_held(&hsr->seqnr_lock);
frame->sequence_nr = hsr->sequence_nr;
hsr->sequence_nr++;
- spin_unlock_irqrestore(&hsr->seqnr_lock, irqflags);
}
}
@@ -571,23 +569,20 @@ static int fill_frame_info(struct hsr_frame_info *frame,
struct ethhdr *ethhdr;
__be16 proto;
int ret;
- u32 hash;
/* Check if skb contains ethhdr */
if (skb->mac_len < sizeof(struct ethhdr))
return -EINVAL;
memset(frame, 0, sizeof(*frame));
-
- ethhdr = (struct ethhdr *)skb_mac_header(skb);
- hash = hsr_mac_hash(port->hsr, ethhdr->h_source);
frame->is_supervision = is_supervision_frame(port->hsr, skb);
- frame->node_src = hsr_get_node(port, &hsr->node_db[hash], skb,
+ frame->node_src = hsr_get_node(port, &hsr->node_db, skb,
frame->is_supervision,
port->type);
if (!frame->node_src)
return -1; /* Unknown node and !is_supervision, or no mem */
+ ethhdr = (struct ethhdr *)skb_mac_header(skb);
frame->is_vlan = false;
proto = ethhdr->h_proto;
@@ -617,11 +612,13 @@ void hsr_forward_skb(struct sk_buff *skb, struct hsr_port *port)
{
struct hsr_frame_info frame;
+ rcu_read_lock();
if (fill_frame_info(&frame, skb, port) < 0)
goto out_drop;
hsr_register_frame_in(frame.node_src, port, frame.sequence_nr);
hsr_forward_do(&frame);
+ rcu_read_unlock();
/* Gets called for ingress frames as well as egress from master port.
* So check and increment stats for master port only here.
*/
@@ -636,6 +633,7 @@ void hsr_forward_skb(struct sk_buff *skb, struct hsr_port *port)
return;
out_drop:
+ rcu_read_unlock();
port->dev->stats.tx_dropped++;
kfree_skb(skb);
}
diff --git a/net/hsr/hsr_framereg.c b/net/hsr/hsr_framereg.c
index 584e21788799..00db74d96583 100644
--- a/net/hsr/hsr_framereg.c
+++ b/net/hsr/hsr_framereg.c
@@ -15,37 +15,10 @@
#include <linux/etherdevice.h>
#include <linux/slab.h>
#include <linux/rculist.h>
-#include <linux/jhash.h>
#include "hsr_main.h"
#include "hsr_framereg.h"
#include "hsr_netlink.h"
-#ifdef CONFIG_LOCKDEP
-int lockdep_hsr_is_held(spinlock_t *lock)
-{
- return lockdep_is_held(lock);
-}
-#endif
-
-u32 hsr_mac_hash(struct hsr_priv *hsr, const unsigned char *addr)
-{
- u32 hash = jhash(addr, ETH_ALEN, hsr->hash_seed);
-
- return reciprocal_scale(hash, hsr->hash_buckets);
-}
-
-struct hsr_node *hsr_node_get_first(struct hlist_head *head, spinlock_t *lock)
-{
- struct hlist_node *first;
-
- first = rcu_dereference_bh_check(hlist_first_rcu(head),
- lockdep_hsr_is_held(lock));
- if (first)
- return hlist_entry(first, struct hsr_node, mac_list);
-
- return NULL;
-}
-
/* seq_nr_after(a, b) - return true if a is after (higher in sequence than) b,
* false otherwise.
*/
@@ -65,30 +38,32 @@ static bool seq_nr_after(u16 a, u16 b)
bool hsr_addr_is_self(struct hsr_priv *hsr, unsigned char *addr)
{
- struct hsr_node *node;
+ struct hsr_self_node *sn;
+ bool ret = false;
- node = hsr_node_get_first(&hsr->self_node_db, &hsr->list_lock);
- if (!node) {
+ rcu_read_lock();
+ sn = rcu_dereference(hsr->self_node);
+ if (!sn) {
WARN_ONCE(1, "HSR: No self node\n");
- return false;
+ goto out;
}
- if (ether_addr_equal(addr, node->macaddress_A))
- return true;
- if (ether_addr_equal(addr, node->macaddress_B))
- return true;
-
- return false;
+ if (ether_addr_equal(addr, sn->macaddress_A) ||
+ ether_addr_equal(addr, sn->macaddress_B))
+ ret = true;
+out:
+ rcu_read_unlock();
+ return ret;
}
/* Search for mac entry. Caller must hold rcu read lock.
*/
-static struct hsr_node *find_node_by_addr_A(struct hlist_head *node_db,
+static struct hsr_node *find_node_by_addr_A(struct list_head *node_db,
const unsigned char addr[ETH_ALEN])
{
struct hsr_node *node;
- hlist_for_each_entry_rcu(node, node_db, mac_list) {
+ list_for_each_entry_rcu(node, node_db, mac_list) {
if (ether_addr_equal(node->macaddress_A, addr))
return node;
}
@@ -96,58 +71,51 @@ static struct hsr_node *find_node_by_addr_A(struct hlist_head *node_db,
return NULL;
}
-/* Helper for device init; the self_node_db is used in hsr_rcv() to recognize
+/* Helper for device init; the self_node is used in hsr_rcv() to recognize
* frames from self that's been looped over the HSR ring.
*/
int hsr_create_self_node(struct hsr_priv *hsr,
const unsigned char addr_a[ETH_ALEN],
const unsigned char addr_b[ETH_ALEN])
{
- struct hlist_head *self_node_db = &hsr->self_node_db;
- struct hsr_node *node, *oldnode;
+ struct hsr_self_node *sn, *old;
- node = kmalloc(sizeof(*node), GFP_KERNEL);
- if (!node)
+ sn = kmalloc(sizeof(*sn), GFP_KERNEL);
+ if (!sn)
return -ENOMEM;
- ether_addr_copy(node->macaddress_A, addr_a);
- ether_addr_copy(node->macaddress_B, addr_b);
+ ether_addr_copy(sn->macaddress_A, addr_a);
+ ether_addr_copy(sn->macaddress_B, addr_b);
spin_lock_bh(&hsr->list_lock);
- oldnode = hsr_node_get_first(self_node_db, &hsr->list_lock);
- if (oldnode) {
- hlist_replace_rcu(&oldnode->mac_list, &node->mac_list);
- spin_unlock_bh(&hsr->list_lock);
- kfree_rcu(oldnode, rcu_head);
- } else {
- hlist_add_tail_rcu(&node->mac_list, self_node_db);
- spin_unlock_bh(&hsr->list_lock);
- }
+ old = rcu_replace_pointer(hsr->self_node, sn,
+ lockdep_is_held(&hsr->list_lock));
+ spin_unlock_bh(&hsr->list_lock);
+ if (old)
+ kfree_rcu(old, rcu_head);
return 0;
}
void hsr_del_self_node(struct hsr_priv *hsr)
{
- struct hlist_head *self_node_db = &hsr->self_node_db;
- struct hsr_node *node;
+ struct hsr_self_node *old;
spin_lock_bh(&hsr->list_lock);
- node = hsr_node_get_first(self_node_db, &hsr->list_lock);
- if (node) {
- hlist_del_rcu(&node->mac_list);
- kfree_rcu(node, rcu_head);
- }
+ old = rcu_replace_pointer(hsr->self_node, NULL,
+ lockdep_is_held(&hsr->list_lock));
spin_unlock_bh(&hsr->list_lock);
+ if (old)
+ kfree_rcu(old, rcu_head);
}
-void hsr_del_nodes(struct hlist_head *node_db)
+void hsr_del_nodes(struct list_head *node_db)
{
struct hsr_node *node;
- struct hlist_node *tmp;
+ struct hsr_node *tmp;
- hlist_for_each_entry_safe(node, tmp, node_db, mac_list)
- kfree_rcu(node, rcu_head);
+ list_for_each_entry_safe(node, tmp, node_db, mac_list)
+ kfree(node);
}
void prp_handle_san_frame(bool san, enum hsr_port_type port,
@@ -168,7 +136,7 @@ void prp_handle_san_frame(bool san, enum hsr_port_type port,
* originating from the newly added node.
*/
static struct hsr_node *hsr_add_node(struct hsr_priv *hsr,
- struct hlist_head *node_db,
+ struct list_head *node_db,
unsigned char addr[],
u16 seq_out, bool san,
enum hsr_port_type rx_port)
@@ -182,6 +150,7 @@ static struct hsr_node *hsr_add_node(struct hsr_priv *hsr,
return NULL;
ether_addr_copy(new_node->macaddress_A, addr);
+ spin_lock_init(&new_node->seq_out_lock);
/* We are only interested in time diffs here, so use current jiffies
* as initialization. (0 could trigger an spurious ring error warning).
@@ -198,14 +167,14 @@ static struct hsr_node *hsr_add_node(struct hsr_priv *hsr,
hsr->proto_ops->handle_san_frame(san, rx_port, new_node);
spin_lock_bh(&hsr->list_lock);
- hlist_for_each_entry_rcu(node, node_db, mac_list,
- lockdep_hsr_is_held(&hsr->list_lock)) {
+ list_for_each_entry_rcu(node, node_db, mac_list,
+ lockdep_is_held(&hsr->list_lock)) {
if (ether_addr_equal(node->macaddress_A, addr))
goto out;
if (ether_addr_equal(node->macaddress_B, addr))
goto out;
}
- hlist_add_tail_rcu(&new_node->mac_list, node_db);
+ list_add_tail_rcu(&new_node->mac_list, node_db);
spin_unlock_bh(&hsr->list_lock);
return new_node;
out:
@@ -225,7 +194,7 @@ void prp_update_san_info(struct hsr_node *node, bool is_sup)
/* Get the hsr_node from which 'skb' was sent.
*/
-struct hsr_node *hsr_get_node(struct hsr_port *port, struct hlist_head *node_db,
+struct hsr_node *hsr_get_node(struct hsr_port *port, struct list_head *node_db,
struct sk_buff *skb, bool is_sup,
enum hsr_port_type rx_port)
{
@@ -241,7 +210,7 @@ struct hsr_node *hsr_get_node(struct hsr_port *port, struct hlist_head *node_db,
ethhdr = (struct ethhdr *)skb_mac_header(skb);
- hlist_for_each_entry_rcu(node, node_db, mac_list) {
+ list_for_each_entry_rcu(node, node_db, mac_list) {
if (ether_addr_equal(node->macaddress_A, ethhdr->h_source)) {
if (hsr->proto_ops->update_san_info)
hsr->proto_ops->update_san_info(node, is_sup);
@@ -291,12 +260,11 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame)
struct hsr_sup_tlv *hsr_sup_tlv;
struct hsr_node *node_real;
struct sk_buff *skb = NULL;
- struct hlist_head *node_db;
+ struct list_head *node_db;
struct ethhdr *ethhdr;
int i;
unsigned int pull_size = 0;
unsigned int total_pull_size = 0;
- u32 hash;
/* Here either frame->skb_hsr or frame->skb_prp should be
* valid as supervision frame always will have protocol
@@ -334,13 +302,11 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame)
hsr_sp = (struct hsr_sup_payload *)skb->data;
/* Merge node_curr (registered on macaddress_B) into node_real */
- node_db = port_rcv->hsr->node_db;
- hash = hsr_mac_hash(hsr, hsr_sp->macaddress_A);
- node_real = find_node_by_addr_A(&node_db[hash], hsr_sp->macaddress_A);
+ node_db = &port_rcv->hsr->node_db;
+ node_real = find_node_by_addr_A(node_db, hsr_sp->macaddress_A);
if (!node_real)
/* No frame received from AddrA of this node yet */
- node_real = hsr_add_node(hsr, &node_db[hash],
- hsr_sp->macaddress_A,
+ node_real = hsr_add_node(hsr, node_db, hsr_sp->macaddress_A,
HSR_SEQNR_START - 1, true,
port_rcv->type);
if (!node_real)
@@ -374,14 +340,14 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame)
hsr_sp = (struct hsr_sup_payload *)skb->data;
/* Check if redbox mac and node mac are equal. */
- if (!ether_addr_equal(node_real->macaddress_A,
- hsr_sp->macaddress_A)) {
+ if (!ether_addr_equal(node_real->macaddress_A, hsr_sp->macaddress_A)) {
/* This is a redbox supervision frame for a VDAN! */
goto done;
}
}
ether_addr_copy(node_real->macaddress_B, ethhdr->h_source);
+ spin_lock_bh(&node_real->seq_out_lock);
for (i = 0; i < HSR_PT_PORTS; i++) {
if (!node_curr->time_in_stale[i] &&
time_after(node_curr->time_in[i], node_real->time_in[i])) {
@@ -392,12 +358,16 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame)
if (seq_nr_after(node_curr->seq_out[i], node_real->seq_out[i]))
node_real->seq_out[i] = node_curr->seq_out[i];
}
+ spin_unlock_bh(&node_real->seq_out_lock);
node_real->addr_B_port = port_rcv->type;
spin_lock_bh(&hsr->list_lock);
- hlist_del_rcu(&node_curr->mac_list);
+ if (!node_curr->removed) {
+ list_del_rcu(&node_curr->mac_list);
+ node_curr->removed = true;
+ kfree_rcu(node_curr, rcu_head);
+ }
spin_unlock_bh(&hsr->list_lock);
- kfree_rcu(node_curr, rcu_head);
done:
/* Push back here */
@@ -433,7 +403,6 @@ void hsr_addr_subst_dest(struct hsr_node *node_src, struct sk_buff *skb,
struct hsr_port *port)
{
struct hsr_node *node_dst;
- u32 hash;
if (!skb_mac_header_was_set(skb)) {
WARN_ONCE(1, "%s: Mac header not set\n", __func__);
@@ -443,8 +412,7 @@ void hsr_addr_subst_dest(struct hsr_node *node_src, struct sk_buff *skb,
if (!is_unicast_ether_addr(eth_hdr(skb)->h_dest))
return;
- hash = hsr_mac_hash(port->hsr, eth_hdr(skb)->h_dest);
- node_dst = find_node_by_addr_A(&port->hsr->node_db[hash],
+ node_dst = find_node_by_addr_A(&port->hsr->node_db,
eth_hdr(skb)->h_dest);
if (!node_dst) {
if (net_ratelimit())
@@ -484,13 +452,17 @@ void hsr_register_frame_in(struct hsr_node *node, struct hsr_port *port,
int hsr_register_frame_out(struct hsr_port *port, struct hsr_node *node,
u16 sequence_nr)
{
+ spin_lock_bh(&node->seq_out_lock);
if (seq_nr_before_or_eq(sequence_nr, node->seq_out[port->type]) &&
time_is_after_jiffies(node->time_out[port->type] +
- msecs_to_jiffies(HSR_ENTRY_FORGET_TIME)))
+ msecs_to_jiffies(HSR_ENTRY_FORGET_TIME))) {
+ spin_unlock_bh(&node->seq_out_lock);
return 1;
+ }
node->time_out[port->type] = jiffies;
node->seq_out[port->type] = sequence_nr;
+ spin_unlock_bh(&node->seq_out_lock);
return 0;
}
@@ -520,71 +492,60 @@ static struct hsr_port *get_late_port(struct hsr_priv *hsr,
void hsr_prune_nodes(struct timer_list *t)
{
struct hsr_priv *hsr = from_timer(hsr, t, prune_timer);
- struct hlist_node *tmp;
struct hsr_node *node;
+ struct hsr_node *tmp;
struct hsr_port *port;
unsigned long timestamp;
unsigned long time_a, time_b;
- int i;
spin_lock_bh(&hsr->list_lock);
+ list_for_each_entry_safe(node, tmp, &hsr->node_db, mac_list) {
+ /* Don't prune own node. Neither time_in[HSR_PT_SLAVE_A]
+ * nor time_in[HSR_PT_SLAVE_B], will ever be updated for
+ * the master port. Thus the master node will be repeatedly
+ * pruned leading to packet loss.
+ */
+ if (hsr_addr_is_self(hsr, node->macaddress_A))
+ continue;
+
+ /* Shorthand */
+ time_a = node->time_in[HSR_PT_SLAVE_A];
+ time_b = node->time_in[HSR_PT_SLAVE_B];
+
+ /* Check for timestamps old enough to risk wrap-around */
+ if (time_after(jiffies, time_a + MAX_JIFFY_OFFSET / 2))
+ node->time_in_stale[HSR_PT_SLAVE_A] = true;
+ if (time_after(jiffies, time_b + MAX_JIFFY_OFFSET / 2))
+ node->time_in_stale[HSR_PT_SLAVE_B] = true;
+
+ /* Get age of newest frame from node.
+ * At least one time_in is OK here; nodes get pruned long
+ * before both time_ins can get stale
+ */
+ timestamp = time_a;
+ if (node->time_in_stale[HSR_PT_SLAVE_A] ||
+ (!node->time_in_stale[HSR_PT_SLAVE_B] &&
+ time_after(time_b, time_a)))
+ timestamp = time_b;
+
+ /* Warn of ring error only as long as we get frames at all */
+ if (time_is_after_jiffies(timestamp +
+ msecs_to_jiffies(1.5 * MAX_SLAVE_DIFF))) {
+ rcu_read_lock();
+ port = get_late_port(hsr, node);
+ if (port)
+ hsr_nl_ringerror(hsr, node->macaddress_A, port);
+ rcu_read_unlock();
+ }
- for (i = 0; i < hsr->hash_buckets; i++) {
- hlist_for_each_entry_safe(node, tmp, &hsr->node_db[i],
- mac_list) {
- /* Don't prune own node.
- * Neither time_in[HSR_PT_SLAVE_A]
- * nor time_in[HSR_PT_SLAVE_B], will ever be updated
- * for the master port. Thus the master node will be
- * repeatedly pruned leading to packet loss.
- */
- if (hsr_addr_is_self(hsr, node->macaddress_A))
- continue;
-
- /* Shorthand */
- time_a = node->time_in[HSR_PT_SLAVE_A];
- time_b = node->time_in[HSR_PT_SLAVE_B];
-
- /* Check for timestamps old enough to
- * risk wrap-around
- */
- if (time_after(jiffies, time_a + MAX_JIFFY_OFFSET / 2))
- node->time_in_stale[HSR_PT_SLAVE_A] = true;
- if (time_after(jiffies, time_b + MAX_JIFFY_OFFSET / 2))
- node->time_in_stale[HSR_PT_SLAVE_B] = true;
-
- /* Get age of newest frame from node.
- * At least one time_in is OK here; nodes get pruned
- * long before both time_ins can get stale
- */
- timestamp = time_a;
- if (node->time_in_stale[HSR_PT_SLAVE_A] ||
- (!node->time_in_stale[HSR_PT_SLAVE_B] &&
- time_after(time_b, time_a)))
- timestamp = time_b;
-
- /* Warn of ring error only as long as we get
- * frames at all
- */
- if (time_is_after_jiffies(timestamp +
- msecs_to_jiffies(1.5 * MAX_SLAVE_DIFF))) {
- rcu_read_lock();
- port = get_late_port(hsr, node);
- if (port)
- hsr_nl_ringerror(hsr,
- node->macaddress_A,
- port);
- rcu_read_unlock();
- }
-
- /* Prune old entries */
- if (time_is_before_jiffies(timestamp +
- msecs_to_jiffies(HSR_NODE_FORGET_TIME))) {
- hsr_nl_nodedown(hsr, node->macaddress_A);
- hlist_del_rcu(&node->mac_list);
- /* Note that we need to free this
- * entry later:
- */
+ /* Prune old entries */
+ if (time_is_before_jiffies(timestamp +
+ msecs_to_jiffies(HSR_NODE_FORGET_TIME))) {
+ hsr_nl_nodedown(hsr, node->macaddress_A);
+ if (!node->removed) {
+ list_del_rcu(&node->mac_list);
+ node->removed = true;
+ /* Note that we need to free this entry later: */
kfree_rcu(node, rcu_head);
}
}
@@ -600,20 +561,17 @@ void *hsr_get_next_node(struct hsr_priv *hsr, void *_pos,
unsigned char addr[ETH_ALEN])
{
struct hsr_node *node;
- u32 hash;
-
- hash = hsr_mac_hash(hsr, addr);
if (!_pos) {
- node = hsr_node_get_first(&hsr->node_db[hash],
- &hsr->list_lock);
+ node = list_first_or_null_rcu(&hsr->node_db,
+ struct hsr_node, mac_list);
if (node)
ether_addr_copy(addr, node->macaddress_A);
return node;
}
node = _pos;
- hlist_for_each_entry_continue_rcu(node, mac_list) {
+ list_for_each_entry_continue_rcu(node, &hsr->node_db, mac_list) {
ether_addr_copy(addr, node->macaddress_A);
return node;
}
@@ -633,11 +591,8 @@ int hsr_get_node_data(struct hsr_priv *hsr,
struct hsr_node *node;
struct hsr_port *port;
unsigned long tdiff;
- u32 hash;
-
- hash = hsr_mac_hash(hsr, addr);
- node = find_node_by_addr_A(&hsr->node_db[hash], addr);
+ node = find_node_by_addr_A(&hsr->node_db, addr);
if (!node)
return -ENOENT;
diff --git a/net/hsr/hsr_framereg.h b/net/hsr/hsr_framereg.h
index f3762e9e42b5..b23556251d62 100644
--- a/net/hsr/hsr_framereg.h
+++ b/net/hsr/hsr_framereg.h
@@ -28,17 +28,9 @@ struct hsr_frame_info {
bool is_from_san;
};
-#ifdef CONFIG_LOCKDEP
-int lockdep_hsr_is_held(spinlock_t *lock);
-#else
-#define lockdep_hsr_is_held(lock) 1
-#endif
-
-u32 hsr_mac_hash(struct hsr_priv *hsr, const unsigned char *addr);
-struct hsr_node *hsr_node_get_first(struct hlist_head *head, spinlock_t *lock);
void hsr_del_self_node(struct hsr_priv *hsr);
-void hsr_del_nodes(struct hlist_head *node_db);
-struct hsr_node *hsr_get_node(struct hsr_port *port, struct hlist_head *node_db,
+void hsr_del_nodes(struct list_head *node_db);
+struct hsr_node *hsr_get_node(struct hsr_port *port, struct list_head *node_db,
struct sk_buff *skb, bool is_sup,
enum hsr_port_type rx_port);
void hsr_handle_sup_frame(struct hsr_frame_info *frame);
@@ -76,7 +68,9 @@ void prp_handle_san_frame(bool san, enum hsr_port_type port,
void prp_update_san_info(struct hsr_node *node, bool is_sup);
struct hsr_node {
- struct hlist_node mac_list;
+ struct list_head mac_list;
+ /* Protect R/W access to seq_out */
+ spinlock_t seq_out_lock;
unsigned char macaddress_A[ETH_ALEN];
unsigned char macaddress_B[ETH_ALEN];
/* Local slave through which AddrB frames are received from this node */
@@ -88,6 +82,7 @@ struct hsr_node {
bool san_a;
bool san_b;
u16 seq_out[HSR_PT_PORTS];
+ bool removed;
struct rcu_head rcu_head;
};
diff --git a/net/hsr/hsr_main.h b/net/hsr/hsr_main.h
index b158ba409f9a..5584c80a5c79 100644
--- a/net/hsr/hsr_main.h
+++ b/net/hsr/hsr_main.h
@@ -47,9 +47,6 @@
#define HSR_V1_SUP_LSDUSIZE 52
-#define HSR_HSIZE_SHIFT 8
-#define HSR_HSIZE BIT(HSR_HSIZE_SHIFT)
-
/* The helper functions below assumes that 'path' occupies the 4 most
* significant bits of the 16-bit field shared by 'path' and 'LSDU_size' (or
* equivalently, the 4 most significant bits of HSR tag byte 14).
@@ -185,11 +182,17 @@ struct hsr_proto_ops {
void (*update_san_info)(struct hsr_node *node, bool is_sup);
};
+struct hsr_self_node {
+ unsigned char macaddress_A[ETH_ALEN];
+ unsigned char macaddress_B[ETH_ALEN];
+ struct rcu_head rcu_head;
+};
+
struct hsr_priv {
struct rcu_head rcu_head;
struct list_head ports;
- struct hlist_head node_db[HSR_HSIZE]; /* Known HSR nodes */
- struct hlist_head self_node_db; /* MACs of slaves */
+ struct list_head node_db; /* Known HSR nodes */
+ struct hsr_self_node __rcu *self_node; /* MACs of slaves */
struct timer_list announce_timer; /* Supervision frame dispatch */
struct timer_list prune_timer;
int announce_count;
@@ -199,8 +202,6 @@ struct hsr_priv {
spinlock_t seqnr_lock; /* locking for sequence_nr */
spinlock_t list_lock; /* locking for node list */
struct hsr_proto_ops *proto_ops;
- u32 hash_buckets;
- u32 hash_seed;
#define PRP_LAN_ID 0x5 /* 0x1010 for A and 0x1011 for B. Bit 0 is set
* based on SLAVE_A or SLAVE_B
*/
diff --git a/net/hsr/hsr_netlink.c b/net/hsr/hsr_netlink.c
index 7174a9092900..78fe40eb9f01 100644
--- a/net/hsr/hsr_netlink.c
+++ b/net/hsr/hsr_netlink.c
@@ -105,7 +105,6 @@ static int hsr_newlink(struct net *src_net, struct net_device *dev,
static void hsr_dellink(struct net_device *dev, struct list_head *head)
{
struct hsr_priv *hsr = netdev_priv(dev);
- int i;
del_timer_sync(&hsr->prune_timer);
del_timer_sync(&hsr->announce_timer);
@@ -114,8 +113,7 @@ static void hsr_dellink(struct net_device *dev, struct list_head *head)
hsr_del_ports(hsr);
hsr_del_self_node(hsr);
- for (i = 0; i < hsr->hash_buckets; i++)
- hsr_del_nodes(&hsr->node_db[i]);
+ hsr_del_nodes(&hsr->node_db);
unregister_netdevice_queue(dev, head);
}
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 24602a5184b0..001947136b0a 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -4464,11 +4464,8 @@ bool tcp_alloc_md5sig_pool(void)
if (unlikely(!READ_ONCE(tcp_md5sig_pool_populated))) {
mutex_lock(&tcp_md5sig_mutex);
- if (!tcp_md5sig_pool_populated) {
+ if (!tcp_md5sig_pool_populated)
__tcp_alloc_md5sig_pool();
- if (tcp_md5sig_pool_populated)
- static_branch_inc(&tcp_md5_needed);
- }
mutex_unlock(&tcp_md5sig_mutex);
}
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 1215fa4c1b9f..8320d0ecb13a 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -1053,7 +1053,7 @@ static void tcp_v4_reqsk_destructor(struct request_sock *req)
* We need to maintain these in the sk structure.
*/
-DEFINE_STATIC_KEY_FALSE(tcp_md5_needed);
+DEFINE_STATIC_KEY_DEFERRED_FALSE(tcp_md5_needed, HZ);
EXPORT_SYMBOL(tcp_md5_needed);
static bool better_md5_match(struct tcp_md5sig_key *old, struct tcp_md5sig_key *new)
@@ -1161,10 +1161,25 @@ struct tcp_md5sig_key *tcp_v4_md5_lookup(const struct sock *sk,
}
EXPORT_SYMBOL(tcp_v4_md5_lookup);
+static int tcp_md5sig_info_add(struct sock *sk, gfp_t gfp)
+{
+ struct tcp_sock *tp = tcp_sk(sk);
+ struct tcp_md5sig_info *md5sig;
+
+ md5sig = kmalloc(sizeof(*md5sig), gfp);
+ if (!md5sig)
+ return -ENOMEM;
+
+ sk_gso_disable(sk);
+ INIT_HLIST_HEAD(&md5sig->head);
+ rcu_assign_pointer(tp->md5sig_info, md5sig);
+ return 0;
+}
+
/* This can be called on a newly created socket, from other files */
-int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr,
- int family, u8 prefixlen, int l3index, u8 flags,
- const u8 *newkey, u8 newkeylen, gfp_t gfp)
+static int __tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr,
+ int family, u8 prefixlen, int l3index, u8 flags,
+ const u8 *newkey, u8 newkeylen, gfp_t gfp)
{
/* Add Key to the list */
struct tcp_md5sig_key *key;
@@ -1193,15 +1208,6 @@ int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr,
md5sig = rcu_dereference_protected(tp->md5sig_info,
lockdep_sock_is_held(sk));
- if (!md5sig) {
- md5sig = kmalloc(sizeof(*md5sig), gfp);
- if (!md5sig)
- return -ENOMEM;
-
- sk_gso_disable(sk);
- INIT_HLIST_HEAD(&md5sig->head);
- rcu_assign_pointer(tp->md5sig_info, md5sig);
- }
key = sock_kmalloc(sk, sizeof(*key), gfp | __GFP_ZERO);
if (!key)
@@ -1223,8 +1229,59 @@ int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr,
hlist_add_head_rcu(&key->node, &md5sig->head);
return 0;
}
+
+int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr,
+ int family, u8 prefixlen, int l3index, u8 flags,
+ const u8 *newkey, u8 newkeylen)
+{
+ struct tcp_sock *tp = tcp_sk(sk);
+
+ if (!rcu_dereference_protected(tp->md5sig_info, lockdep_sock_is_held(sk))) {
+ if (tcp_md5sig_info_add(sk, GFP_KERNEL))
+ return -ENOMEM;
+
+ if (!static_branch_inc(&tcp_md5_needed.key)) {
+ struct tcp_md5sig_info *md5sig;
+
+ md5sig = rcu_dereference_protected(tp->md5sig_info, lockdep_sock_is_held(sk));
+ rcu_assign_pointer(tp->md5sig_info, NULL);
+ kfree_rcu(md5sig, rcu);
+ return -EUSERS;
+ }
+ }
+
+ return __tcp_md5_do_add(sk, addr, family, prefixlen, l3index, flags,
+ newkey, newkeylen, GFP_KERNEL);
+}
EXPORT_SYMBOL(tcp_md5_do_add);
+int tcp_md5_key_copy(struct sock *sk, const union tcp_md5_addr *addr,
+ int family, u8 prefixlen, int l3index,
+ struct tcp_md5sig_key *key)
+{
+ struct tcp_sock *tp = tcp_sk(sk);
+
+ if (!rcu_dereference_protected(tp->md5sig_info, lockdep_sock_is_held(sk))) {
+ if (tcp_md5sig_info_add(sk, sk_gfp_mask(sk, GFP_ATOMIC)))
+ return -ENOMEM;
+
+ if (!static_key_fast_inc_not_disabled(&tcp_md5_needed.key.key)) {
+ struct tcp_md5sig_info *md5sig;
+
+ md5sig = rcu_dereference_protected(tp->md5sig_info, lockdep_sock_is_held(sk));
+ net_warn_ratelimited("Too many TCP-MD5 keys in the system\n");
+ rcu_assign_pointer(tp->md5sig_info, NULL);
+ kfree_rcu(md5sig, rcu);
+ return -EUSERS;
+ }
+ }
+
+ return __tcp_md5_do_add(sk, addr, family, prefixlen, l3index,
+ key->flags, key->key, key->keylen,
+ sk_gfp_mask(sk, GFP_ATOMIC));
+}
+EXPORT_SYMBOL(tcp_md5_key_copy);
+
int tcp_md5_do_del(struct sock *sk, const union tcp_md5_addr *addr, int family,
u8 prefixlen, int l3index, u8 flags)
{
@@ -1311,7 +1368,7 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, int optname,
return -EINVAL;
return tcp_md5_do_add(sk, addr, AF_INET, prefixlen, l3index, flags,
- cmd.tcpm_key, cmd.tcpm_keylen, GFP_KERNEL);
+ cmd.tcpm_key, cmd.tcpm_keylen);
}
static int tcp_v4_md5_hash_headers(struct tcp_md5sig_pool *hp,
@@ -1562,14 +1619,8 @@ struct sock *tcp_v4_syn_recv_sock(const struct sock *sk, struct sk_buff *skb,
addr = (union tcp_md5_addr *)&newinet->inet_daddr;
key = tcp_md5_do_lookup(sk, l3index, addr, AF_INET);
if (key) {
- /*
- * We're using one, so create a matching key
- * on the newsk structure. If we fail to get
- * memory, then we end up not copying the key
- * across. Shucks.
- */
- tcp_md5_do_add(newsk, addr, AF_INET, 32, l3index, key->flags,
- key->key, key->keylen, GFP_ATOMIC);
+ if (tcp_md5_key_copy(newsk, addr, AF_INET, 32, l3index, key))
+ goto put_and_exit;
sk_gso_disable(newsk);
}
#endif
@@ -2261,6 +2312,7 @@ void tcp_v4_destroy_sock(struct sock *sk)
tcp_clear_md5_list(sk);
kfree_rcu(rcu_dereference_protected(tp->md5sig_info, 1), rcu);
tp->md5sig_info = NULL;
+ static_branch_slow_dec_deferred(&tcp_md5_needed);
}
#endif
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
index c375f603a16c..e002f2e1d4f2 100644
--- a/net/ipv4/tcp_minisocks.c
+++ b/net/ipv4/tcp_minisocks.c
@@ -240,6 +240,40 @@ kill:
}
EXPORT_SYMBOL(tcp_timewait_state_process);
+static void tcp_time_wait_init(struct sock *sk, struct tcp_timewait_sock *tcptw)
+{
+#ifdef CONFIG_TCP_MD5SIG
+ const struct tcp_sock *tp = tcp_sk(sk);
+ struct tcp_md5sig_key *key;
+
+ /*
+ * The timewait bucket does not have the key DB from the
+ * sock structure. We just make a quick copy of the
+ * md5 key being used (if indeed we are using one)
+ * so the timewait ack generating code has the key.
+ */
+ tcptw->tw_md5_key = NULL;
+ if (!static_branch_unlikely(&tcp_md5_needed.key))
+ return;
+
+ key = tp->af_specific->md5_lookup(sk, sk);
+ if (key) {
+ tcptw->tw_md5_key = kmemdup(key, sizeof(*key), GFP_ATOMIC);
+ if (!tcptw->tw_md5_key)
+ return;
+ if (!tcp_alloc_md5sig_pool())
+ goto out_free;
+ if (!static_key_fast_inc_not_disabled(&tcp_md5_needed.key.key))
+ goto out_free;
+ }
+ return;
+out_free:
+ WARN_ON_ONCE(1);
+ kfree(tcptw->tw_md5_key);
+ tcptw->tw_md5_key = NULL;
+#endif
+}
+
/*
* Move a socket to time-wait or dead fin-wait-2 state.
*/
@@ -282,26 +316,7 @@ void tcp_time_wait(struct sock *sk, int state, int timeo)
}
#endif
-#ifdef CONFIG_TCP_MD5SIG
- /*
- * The timewait bucket does not have the key DB from the
- * sock structure. We just make a quick copy of the
- * md5 key being used (if indeed we are using one)
- * so the timewait ack generating code has the key.
- */
- do {
- tcptw->tw_md5_key = NULL;
- if (static_branch_unlikely(&tcp_md5_needed)) {
- struct tcp_md5sig_key *key;
-
- key = tp->af_specific->md5_lookup(sk, sk);
- if (key) {
- tcptw->tw_md5_key = kmemdup(key, sizeof(*key), GFP_ATOMIC);
- BUG_ON(tcptw->tw_md5_key && !tcp_alloc_md5sig_pool());
- }
- }
- } while (0);
-#endif
+ tcp_time_wait_init(sk, tcptw);
/* Get the TIME_WAIT timeout firing. */
if (timeo < rto)
@@ -337,11 +352,13 @@ EXPORT_SYMBOL(tcp_time_wait);
void tcp_twsk_destructor(struct sock *sk)
{
#ifdef CONFIG_TCP_MD5SIG
- if (static_branch_unlikely(&tcp_md5_needed)) {
+ if (static_branch_unlikely(&tcp_md5_needed.key)) {
struct tcp_timewait_sock *twsk = tcp_twsk(sk);
- if (twsk->tw_md5_key)
+ if (twsk->tw_md5_key) {
kfree_rcu(twsk->tw_md5_key, rcu);
+ static_branch_slow_dec_deferred(&tcp_md5_needed);
+ }
}
#endif
}
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 894410dc9293..71d01cf3c13e 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -766,7 +766,7 @@ static unsigned int tcp_syn_options(struct sock *sk, struct sk_buff *skb,
*md5 = NULL;
#ifdef CONFIG_TCP_MD5SIG
- if (static_branch_unlikely(&tcp_md5_needed) &&
+ if (static_branch_unlikely(&tcp_md5_needed.key) &&
rcu_access_pointer(tp->md5sig_info)) {
*md5 = tp->af_specific->md5_lookup(sk, sk);
if (*md5) {
@@ -922,7 +922,7 @@ static unsigned int tcp_established_options(struct sock *sk, struct sk_buff *skb
*md5 = NULL;
#ifdef CONFIG_TCP_MD5SIG
- if (static_branch_unlikely(&tcp_md5_needed) &&
+ if (static_branch_unlikely(&tcp_md5_needed.key) &&
rcu_access_pointer(tp->md5sig_info)) {
*md5 = tp->af_specific->md5_lookup(sk, sk);
if (*md5) {
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index f52b6f271a24..11b736a76bd7 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -665,12 +665,11 @@ static int tcp_v6_parse_md5_keys(struct sock *sk, int optname,
if (ipv6_addr_v4mapped(&sin6->sin6_addr))
return tcp_md5_do_add(sk, (union tcp_md5_addr *)&sin6->sin6_addr.s6_addr32[3],
AF_INET, prefixlen, l3index, flags,
- cmd.tcpm_key, cmd.tcpm_keylen,
- GFP_KERNEL);
+ cmd.tcpm_key, cmd.tcpm_keylen);
return tcp_md5_do_add(sk, (union tcp_md5_addr *)&sin6->sin6_addr,
AF_INET6, prefixlen, l3index, flags,
- cmd.tcpm_key, cmd.tcpm_keylen, GFP_KERNEL);
+ cmd.tcpm_key, cmd.tcpm_keylen);
}
static int tcp_v6_md5_hash_headers(struct tcp_md5sig_pool *hp,
@@ -1365,14 +1364,14 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff *
/* Copy over the MD5 key from the original socket */
key = tcp_v6_md5_do_lookup(sk, &newsk->sk_v6_daddr, l3index);
if (key) {
- /* We're using one, so create a matching key
- * on the newsk structure. If we fail to get
- * memory, then we end up not copying the key
- * across. Shucks.
- */
- tcp_md5_do_add(newsk, (union tcp_md5_addr *)&newsk->sk_v6_daddr,
- AF_INET6, 128, l3index, key->flags, key->key, key->keylen,
- sk_gfp_mask(sk, GFP_ATOMIC));
+ const union tcp_md5_addr *addr;
+
+ addr = (union tcp_md5_addr *)&newsk->sk_v6_daddr;
+ if (tcp_md5_key_copy(newsk, addr, AF_INET6, 128, l3index, key)) {
+ inet_csk_prepare_forced_close(newsk);
+ tcp_done(newsk);
+ goto out;
+ }
}
#endif
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index c848fe04dd44..8f9a2ab502b3 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -576,7 +576,7 @@ static struct ieee80211_key *
ieee80211_lookup_key(struct ieee80211_sub_if_data *sdata, int link_id,
u8 key_idx, bool pairwise, const u8 *mac_addr)
{
- struct ieee80211_local *local = sdata->local;
+ struct ieee80211_local *local __maybe_unused = sdata->local;
struct ieee80211_link_data *link = &sdata->deflink;
struct ieee80211_key *key;
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 7c4ce716c939..d49a5906a943 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -1849,8 +1849,7 @@ static int ieee80211_runtime_change_iftype(struct ieee80211_sub_if_data *sdata,
ieee80211_stop_vif_queues(local, sdata,
IEEE80211_QUEUE_STOP_REASON_IFTYPE_CHANGE);
- synchronize_net();
-
+ /* do_stop will synchronize_rcu() first thing */
ieee80211_do_stop(sdata, false);
ieee80211_teardown_sdata(sdata);
@@ -2179,6 +2178,7 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
ndev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
ndev->hw_features |= ndev->features &
MAC80211_SUPPORTED_FEATURES_TX;
+ sdata->vif.netdev_features = local->hw.netdev_features;
netdev_set_default_ethtool_ops(ndev, &ieee80211_ethtool_ops);
@@ -2195,6 +2195,7 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
ret = cfg80211_register_netdevice(ndev);
if (ret) {
+ ieee80211_if_free(ndev);
free_netdev(ndev);
return ret;
}
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index a804e0220ed7..0aee2392dd29 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -3932,7 +3932,6 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link,
struct ieee80211_elems_parse_params parse_params = {
.start = elem_start,
.len = elem_len,
- .bss = cbss,
.link_id = link_id == assoc_data->assoc_link_id ? -1 : link_id,
.from_ap = true,
};
@@ -4017,6 +4016,7 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link,
parse_params.start = bss_ies->data;
parse_params.len = bss_ies->len;
+ parse_params.bss = cbss;
bss_elems = ieee802_11_parse_elems_full(&parse_params);
if (!bss_elems) {
ret = false;
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index c28c6fbf786e..7e3ab6e1b28f 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -2403,7 +2403,6 @@ static int ieee80211_802_1x_port_control(struct ieee80211_rx_data *rx)
static int ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx, __le16 fc)
{
- struct ieee80211_hdr *hdr = (void *)rx->skb->data;
struct sk_buff *skb = rx->skb;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
@@ -2414,31 +2413,6 @@ static int ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx, __le16 fc)
if (status->flag & RX_FLAG_DECRYPTED)
return 0;
- /* check mesh EAPOL frames first */
- if (unlikely(rx->sta && ieee80211_vif_is_mesh(&rx->sdata->vif) &&
- ieee80211_is_data(fc))) {
- struct ieee80211s_hdr *mesh_hdr;
- u16 hdr_len = ieee80211_hdrlen(fc);
- u16 ethertype_offset;
- __be16 ethertype;
-
- if (!ether_addr_equal(hdr->addr1, rx->sdata->vif.addr))
- goto drop_check;
-
- /* make sure fixed part of mesh header is there, also checks skb len */
- if (!pskb_may_pull(rx->skb, hdr_len + 6))
- goto drop_check;
-
- mesh_hdr = (struct ieee80211s_hdr *)(skb->data + hdr_len);
- ethertype_offset = hdr_len + ieee80211_get_mesh_hdrlen(mesh_hdr) +
- sizeof(rfc1042_header);
-
- if (skb_copy_bits(rx->skb, ethertype_offset, &ethertype, 2) == 0 &&
- ethertype == rx->sdata->control_port_protocol)
- return 0;
- }
-
-drop_check:
/* Drop unencrypted frames if key is set. */
if (unlikely(!ieee80211_has_protected(fc) &&
!ieee80211_is_any_nullfunc(fc) &&
@@ -2892,8 +2866,16 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
hdr = (struct ieee80211_hdr *) skb->data;
mesh_hdr = (struct ieee80211s_hdr *) (skb->data + hdrlen);
- if (ieee80211_drop_unencrypted(rx, hdr->frame_control))
- return RX_DROP_MONITOR;
+ if (ieee80211_drop_unencrypted(rx, hdr->frame_control)) {
+ int offset = hdrlen + ieee80211_get_mesh_hdrlen(mesh_hdr) +
+ sizeof(rfc1042_header);
+ __be16 ethertype;
+
+ if (!ether_addr_equal(hdr->addr1, rx->sdata->vif.addr) ||
+ skb_copy_bits(rx->skb, offset, &ethertype, 2) != 0 ||
+ ethertype != rx->sdata->control_port_protocol)
+ return RX_DROP_MONITOR;
+ }
/* frame is in RMC, don't forward */
if (ieee80211_is_data(hdr->frame_control) &&
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 165ac0711d71..2171cd1ca807 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -1343,7 +1343,7 @@ static struct txq_info *ieee80211_get_txq(struct ieee80211_local *local,
return NULL;
txq = sta->sta.txq[tid];
- } else if (vif) {
+ } else {
txq = vif->txq;
}
@@ -1355,7 +1355,11 @@ static struct txq_info *ieee80211_get_txq(struct ieee80211_local *local,
static void ieee80211_set_skb_enqueue_time(struct sk_buff *skb)
{
- IEEE80211_SKB_CB(skb)->control.enqueue_time = codel_get_time();
+ struct sk_buff *next;
+ codel_time_t now = codel_get_time();
+
+ skb_list_walk_safe(skb, skb, next)
+ IEEE80211_SKB_CB(skb)->control.enqueue_time = now;
}
static u32 codel_skb_len_func(const struct sk_buff *skb)
@@ -3578,55 +3582,79 @@ ieee80211_xmit_fast_finish(struct ieee80211_sub_if_data *sdata,
return TX_CONTINUE;
}
-static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
- struct sta_info *sta,
- struct ieee80211_fast_tx *fast_tx,
- struct sk_buff *skb)
+static netdev_features_t
+ieee80211_sdata_netdev_features(struct ieee80211_sub_if_data *sdata)
{
- struct ieee80211_local *local = sdata->local;
- u16 ethertype = (skb->data[12] << 8) | skb->data[13];
- int extra_head = fast_tx->hdr_len - (ETH_HLEN - 2);
- int hw_headroom = sdata->local->hw.extra_tx_headroom;
- struct ethhdr eth;
- struct ieee80211_tx_info *info;
- struct ieee80211_hdr *hdr = (void *)fast_tx->hdr;
- struct ieee80211_tx_data tx;
- ieee80211_tx_result r;
- struct tid_ampdu_tx *tid_tx = NULL;
- u8 tid = IEEE80211_NUM_TIDS;
+ if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN)
+ return sdata->vif.netdev_features;
- /* control port protocol needs a lot of special handling */
- if (cpu_to_be16(ethertype) == sdata->control_port_protocol)
- return false;
+ if (!sdata->bss)
+ return 0;
- /* only RFC 1042 SNAP */
- if (ethertype < ETH_P_802_3_MIN)
- return false;
+ sdata = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap);
+ return sdata->vif.netdev_features;
+}
- /* don't handle TX status request here either */
- if (skb->sk && skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS)
- return false;
+static struct sk_buff *
+ieee80211_tx_skb_fixup(struct sk_buff *skb, netdev_features_t features)
+{
+ if (skb_is_gso(skb)) {
+ struct sk_buff *segs;
- if (hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) {
- tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
- tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[tid]);
- if (tid_tx) {
- if (!test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state))
- return false;
- if (tid_tx->timeout)
- tid_tx->last_tx = jiffies;
- }
+ segs = skb_gso_segment(skb, features);
+ if (!segs)
+ return skb;
+ if (IS_ERR(segs))
+ goto free;
+
+ consume_skb(skb);
+ return segs;
}
- /* after this point (skb is modified) we cannot return false */
+ if (skb_needs_linearize(skb, features) && __skb_linearize(skb))
+ goto free;
+
+ if (skb->ip_summed == CHECKSUM_PARTIAL) {
+ int ofs = skb_checksum_start_offset(skb);
+
+ if (skb->encapsulation)
+ skb_set_inner_transport_header(skb, ofs);
+ else
+ skb_set_transport_header(skb, ofs);
+
+ if (skb_csum_hwoffload_help(skb, features))
+ goto free;
+ }
+
+ skb_mark_not_on_list(skb);
+ return skb;
+
+free:
+ kfree_skb(skb);
+ return NULL;
+}
+
+static void __ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
+ struct sta_info *sta,
+ struct ieee80211_fast_tx *fast_tx,
+ struct sk_buff *skb, u8 tid, bool ampdu)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_hdr *hdr = (void *)fast_tx->hdr;
+ struct ieee80211_tx_info *info;
+ struct ieee80211_tx_data tx;
+ ieee80211_tx_result r;
+ int hw_headroom = sdata->local->hw.extra_tx_headroom;
+ int extra_head = fast_tx->hdr_len - (ETH_HLEN - 2);
+ struct ethhdr eth;
skb = skb_share_check(skb, GFP_ATOMIC);
if (unlikely(!skb))
- return true;
+ return;
if ((hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) &&
ieee80211_amsdu_aggregate(sdata, sta, fast_tx, skb))
- return true;
+ return;
/* will not be crypto-handled beyond what we do here, so use false
* as the may-encrypt argument for the resize to not account for
@@ -3635,10 +3663,8 @@ static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
if (unlikely(ieee80211_skb_resize(sdata, skb,
max_t(int, extra_head + hw_headroom -
skb_headroom(skb), 0),
- ENCRYPT_NO))) {
- kfree_skb(skb);
- return true;
- }
+ ENCRYPT_NO)))
+ goto free;
memcpy(&eth, skb->data, ETH_HLEN - 2);
hdr = skb_push(skb, extra_head);
@@ -3652,7 +3678,7 @@ static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
info->control.vif = &sdata->vif;
info->flags = IEEE80211_TX_CTL_FIRST_FRAGMENT |
IEEE80211_TX_CTL_DONTFRAG |
- (tid_tx ? IEEE80211_TX_CTL_AMPDU : 0);
+ (ampdu ? IEEE80211_TX_CTL_AMPDU : 0);
info->control.flags = IEEE80211_TX_CTRL_FAST_XMIT |
u32_encode_bits(IEEE80211_LINK_UNSPECIFIED,
IEEE80211_TX_CTRL_MLO_LINK);
@@ -3676,16 +3702,14 @@ static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
tx.key = fast_tx->key;
if (ieee80211_queue_skb(local, sdata, sta, skb))
- return true;
+ return;
tx.skb = skb;
r = ieee80211_xmit_fast_finish(sdata, sta, fast_tx->pn_offs,
fast_tx->key, &tx);
tx.skb = NULL;
- if (r == TX_DROP) {
- kfree_skb(skb);
- return true;
- }
+ if (r == TX_DROP)
+ goto free;
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
sdata = container_of(sdata->bss,
@@ -3693,6 +3717,56 @@ static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
__skb_queue_tail(&tx.skbs, skb);
ieee80211_tx_frags(local, &sdata->vif, sta, &tx.skbs, false);
+ return;
+
+free:
+ kfree_skb(skb);
+}
+
+static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
+ struct sta_info *sta,
+ struct ieee80211_fast_tx *fast_tx,
+ struct sk_buff *skb)
+{
+ u16 ethertype = (skb->data[12] << 8) | skb->data[13];
+ struct ieee80211_hdr *hdr = (void *)fast_tx->hdr;
+ struct tid_ampdu_tx *tid_tx = NULL;
+ struct sk_buff *next;
+ u8 tid = IEEE80211_NUM_TIDS;
+
+ /* control port protocol needs a lot of special handling */
+ if (cpu_to_be16(ethertype) == sdata->control_port_protocol)
+ return false;
+
+ /* only RFC 1042 SNAP */
+ if (ethertype < ETH_P_802_3_MIN)
+ return false;
+
+ /* don't handle TX status request here either */
+ if (skb->sk && skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS)
+ return false;
+
+ if (hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) {
+ tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
+ tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[tid]);
+ if (tid_tx) {
+ if (!test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state))
+ return false;
+ if (tid_tx->timeout)
+ tid_tx->last_tx = jiffies;
+ }
+ }
+
+ /* after this point (skb is modified) we cannot return false */
+ skb = ieee80211_tx_skb_fixup(skb, ieee80211_sdata_netdev_features(sdata));
+ if (!skb)
+ return true;
+
+ skb_list_walk_safe(skb, skb, next) {
+ skb_mark_not_on_list(skb);
+ __ieee80211_xmit_fast(sdata, sta, fast_tx, skb, tid, tid_tx);
+ }
+
return true;
}
@@ -4192,31 +4266,14 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb,
goto out;
}
- if (skb_is_gso(skb)) {
- struct sk_buff *segs;
-
- segs = skb_gso_segment(skb, 0);
- if (IS_ERR(segs)) {
- goto out_free;
- } else if (segs) {
- consume_skb(skb);
- skb = segs;
- }
- } else {
- /* we cannot process non-linear frames on this path */
- if (skb_linearize(skb))
- goto out_free;
-
- /* the frame could be fragmented, software-encrypted, and other
- * things so we cannot really handle checksum offload with it -
- * fix it up in software before we handle anything else.
- */
- if (skb->ip_summed == CHECKSUM_PARTIAL) {
- skb_set_transport_header(skb,
- skb_checksum_start_offset(skb));
- if (skb_checksum_help(skb))
- goto out_free;
- }
+ /* the frame could be fragmented, software-encrypted, and other
+ * things so we cannot really handle checksum or GSO offload.
+ * fix it up in software before we handle anything else.
+ */
+ skb = ieee80211_tx_skb_fixup(skb, 0);
+ if (!skb) {
+ len = 0;
+ goto out;
}
skb_list_walk_safe(skb, skb, next) {
@@ -4434,9 +4491,11 @@ normal:
return NETDEV_TX_OK;
}
-static bool ieee80211_tx_8023(struct ieee80211_sub_if_data *sdata,
- struct sk_buff *skb, struct sta_info *sta,
- bool txpending)
+
+
+static bool __ieee80211_tx_8023(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb, struct sta_info *sta,
+ bool txpending)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_tx_control control = {};
@@ -4445,14 +4504,6 @@ static bool ieee80211_tx_8023(struct ieee80211_sub_if_data *sdata,
unsigned long flags;
int q = info->hw_queue;
- if (sta)
- sk_pacing_shift_update(skb->sk, local->hw.tx_sk_pacing_shift);
-
- ieee80211_tpt_led_trig_tx(local, skb->len);
-
- if (ieee80211_queue_skb(local, sdata, sta, skb))
- return true;
-
spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
if (local->queue_stop_reasons[q] ||
@@ -4479,6 +4530,26 @@ static bool ieee80211_tx_8023(struct ieee80211_sub_if_data *sdata,
return true;
}
+static bool ieee80211_tx_8023(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb, struct sta_info *sta,
+ bool txpending)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct sk_buff *next;
+ bool ret = true;
+
+ if (ieee80211_queue_skb(local, sdata, sta, skb))
+ return true;
+
+ skb_list_walk_safe(skb, skb, next) {
+ skb_mark_not_on_list(skb);
+ if (!__ieee80211_tx_8023(sdata, skb, sta, txpending))
+ ret = false;
+ }
+
+ return ret;
+}
+
static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata,
struct net_device *dev, struct sta_info *sta,
struct ieee80211_key *key, struct sk_buff *skb)
@@ -4486,9 +4557,13 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata,
struct ieee80211_tx_info *info;
struct ieee80211_local *local = sdata->local;
struct tid_ampdu_tx *tid_tx;
+ struct sk_buff *seg, *next;
+ unsigned int skbs = 0, len = 0;
+ u16 queue;
u8 tid;
- skb_set_queue_mapping(skb, ieee80211_select_queue(sdata, sta, skb));
+ queue = ieee80211_select_queue(sdata, sta, skb);
+ skb_set_queue_mapping(skb, queue);
if (unlikely(test_bit(SCAN_SW_SCANNING, &local->scanning)) &&
test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state))
@@ -4498,9 +4573,6 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata,
if (unlikely(!skb))
return;
- info = IEEE80211_SKB_CB(skb);
- memset(info, 0, sizeof(*info));
-
ieee80211_aggr_check(sdata, sta, skb);
tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
@@ -4514,22 +4586,20 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata,
return;
}
- info->flags |= IEEE80211_TX_CTL_AMPDU;
if (tid_tx->timeout)
tid_tx->last_tx = jiffies;
}
- if (unlikely(skb->sk &&
- skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS))
- info->ack_frame_id = ieee80211_store_ack_skb(local, skb,
- &info->flags, NULL);
-
- info->hw_queue = sdata->vif.hw_queue[skb_get_queue_mapping(skb)];
+ skb = ieee80211_tx_skb_fixup(skb, ieee80211_sdata_netdev_features(sdata));
+ if (!skb)
+ return;
- dev_sw_netstats_tx_add(dev, 1, skb->len);
+ info = IEEE80211_SKB_CB(skb);
+ memset(info, 0, sizeof(*info));
+ if (tid_tx)
+ info->flags |= IEEE80211_TX_CTL_AMPDU;
- sta->deflink.tx_stats.bytes[skb_get_queue_mapping(skb)] += skb->len;
- sta->deflink.tx_stats.packets[skb_get_queue_mapping(skb)]++;
+ info->hw_queue = sdata->vif.hw_queue[queue];
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
sdata = container_of(sdata->bss,
@@ -4541,6 +4611,24 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata,
if (key)
info->control.hw_key = &key->conf;
+ skb_list_walk_safe(skb, seg, next) {
+ skbs++;
+ len += seg->len;
+ if (seg != skb)
+ memcpy(IEEE80211_SKB_CB(seg), info, sizeof(*info));
+ }
+
+ if (unlikely(skb->sk &&
+ skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS))
+ info->ack_frame_id = ieee80211_store_ack_skb(local, skb,
+ &info->flags, NULL);
+
+ dev_sw_netstats_tx_add(dev, skbs, len);
+ sta->deflink.tx_stats.packets[queue] += skbs;
+ sta->deflink.tx_stats.bytes[queue] += len;
+
+ ieee80211_tpt_led_trig_tx(local, len);
+
ieee80211_tx_8023(sdata, skb, sta, false);
return;
@@ -4582,6 +4670,7 @@ netdev_tx_t ieee80211_subif_start_xmit_8023(struct sk_buff *skb,
key->conf.cipher == WLAN_CIPHER_SUITE_TKIP))
goto skip_offload;
+ sk_pacing_shift_update(skb->sk, sdata->local->hw.tx_sk_pacing_shift);
ieee80211_8023_xmit(sdata, dev, sta, key, skb);
goto out;
@@ -4774,9 +4863,9 @@ static void __ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,
ps->dtim_count--;
}
- tim = pos = skb_put(skb, 6);
+ tim = pos = skb_put(skb, 5);
*pos++ = WLAN_EID_TIM;
- *pos++ = 4;
+ *pos++ = 3;
*pos++ = ps->dtim_count;
*pos++ = link_conf->dtim_period;
@@ -4807,13 +4896,17 @@ static void __ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,
/* Bitmap control */
*pos++ = n1 | aid0;
/* Part Virt Bitmap */
- skb_put(skb, n2 - n1);
- memcpy(pos, ps->tim + n1, n2 - n1 + 1);
+ skb_put_data(skb, ps->tim + n1, n2 - n1 + 1);
tim[1] = n2 - n1 + 4;
} else {
*pos++ = aid0; /* Bitmap control */
- *pos++ = 0; /* Part Virt Bitmap */
+
+ if (ieee80211_get_link_sband(link)->band != NL80211_BAND_S1GHZ) {
+ tim[1] = 4;
+ /* Part Virt Bitmap */
+ skb_put_u8(skb, 0);
+ }
}
}
diff --git a/net/mptcp/pm_netlink.c b/net/mptcp/pm_netlink.c
index d66fbd558263..eef69d0e44ec 100644
--- a/net/mptcp/pm_netlink.c
+++ b/net/mptcp/pm_netlink.c
@@ -1029,6 +1029,8 @@ static int mptcp_pm_nl_create_listen_socket(struct sock *sk,
if (err)
return err;
+ mptcp_event_pm_listener(ssock->sk, MPTCP_EVENT_LISTENER_CREATED);
+
return 0;
}
@@ -2152,6 +2154,58 @@ nla_put_failure:
kfree_skb(skb);
}
+void mptcp_event_pm_listener(const struct sock *ssk,
+ enum mptcp_event_type event)
+{
+ const struct inet_sock *issk = inet_sk(ssk);
+ struct net *net = sock_net(ssk);
+ struct nlmsghdr *nlh;
+ struct sk_buff *skb;
+
+ if (!genl_has_listeners(&mptcp_genl_family, net, MPTCP_PM_EV_GRP_OFFSET))
+ return;
+
+ skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!skb)
+ return;
+
+ nlh = genlmsg_put(skb, 0, 0, &mptcp_genl_family, 0, event);
+ if (!nlh)
+ goto nla_put_failure;
+
+ if (nla_put_u16(skb, MPTCP_ATTR_FAMILY, ssk->sk_family))
+ goto nla_put_failure;
+
+ if (nla_put_be16(skb, MPTCP_ATTR_SPORT, issk->inet_sport))
+ goto nla_put_failure;
+
+ switch (ssk->sk_family) {
+ case AF_INET:
+ if (nla_put_in_addr(skb, MPTCP_ATTR_SADDR4, issk->inet_saddr))
+ goto nla_put_failure;
+ break;
+#if IS_ENABLED(CONFIG_MPTCP_IPV6)
+ case AF_INET6: {
+ const struct ipv6_pinfo *np = inet6_sk(ssk);
+
+ if (nla_put_in6_addr(skb, MPTCP_ATTR_SADDR6, &np->saddr))
+ goto nla_put_failure;
+ break;
+ }
+#endif
+ default:
+ WARN_ON_ONCE(1);
+ goto nla_put_failure;
+ }
+
+ genlmsg_end(skb, nlh);
+ mptcp_nl_mcast_send(net, skb, GFP_KERNEL);
+ return;
+
+nla_put_failure:
+ kfree_skb(skb);
+}
+
void mptcp_event(enum mptcp_event_type type, const struct mptcp_sock *msk,
const struct sock *ssk, gfp_t gfp)
{
@@ -2197,6 +2251,9 @@ void mptcp_event(enum mptcp_event_type type, const struct mptcp_sock *msk,
if (mptcp_event_sub_closed(skb, msk, ssk) < 0)
goto nla_put_failure;
break;
+ case MPTCP_EVENT_LISTENER_CREATED:
+ case MPTCP_EVENT_LISTENER_CLOSED:
+ break;
}
genlmsg_end(skb, nlh);
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index b0d387be500a..f6f93957275b 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -2355,6 +2355,7 @@ static void __mptcp_close_ssk(struct sock *sk, struct sock *ssk,
tcp_set_state(ssk, TCP_CLOSE);
mptcp_subflow_queue_clean(ssk);
inet_csk_listen_stop(ssk);
+ mptcp_event_pm_listener(ssk, MPTCP_EVENT_LISTENER_CLOSED);
}
__tcp_close(ssk, 0);
@@ -3647,6 +3648,8 @@ static int mptcp_listen(struct socket *sock, int backlog)
if (!err)
mptcp_copy_inaddrs(sock->sk, ssock->sk);
+ mptcp_event_pm_listener(ssock->sk, MPTCP_EVENT_LISTENER_CREATED);
+
unlock:
release_sock(sock->sk);
return err;
diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
index 8b4379a2cd85..955fb3d88eb3 100644
--- a/net/mptcp/protocol.h
+++ b/net/mptcp/protocol.h
@@ -839,6 +839,8 @@ void mptcp_event(enum mptcp_event_type type, const struct mptcp_sock *msk,
const struct sock *ssk, gfp_t gfp);
void mptcp_event_addr_announced(const struct sock *ssk, const struct mptcp_addr_info *info);
void mptcp_event_addr_removed(const struct mptcp_sock *msk, u8 id);
+void mptcp_event_pm_listener(const struct sock *ssk,
+ enum mptcp_event_type event);
bool mptcp_userspace_pm_active(const struct mptcp_sock *msk);
void mptcp_fastopen_gen_msk_ackseq(struct mptcp_sock *msk, struct mptcp_subflow_context *subflow,
diff --git a/net/sctp/stream_sched.c b/net/sctp/stream_sched.c
index 7c8f9d89e16a..330067002deb 100644
--- a/net/sctp/stream_sched.c
+++ b/net/sctp/stream_sched.c
@@ -50,10 +50,6 @@ static void sctp_sched_fcfs_free_sid(struct sctp_stream *stream, __u16 sid)
{
}
-static void sctp_sched_fcfs_free(struct sctp_stream *stream)
-{
-}
-
static void sctp_sched_fcfs_enqueue(struct sctp_outq *q,
struct sctp_datamsg *msg)
{
@@ -101,7 +97,6 @@ static struct sctp_sched_ops sctp_sched_fcfs = {
.init = sctp_sched_fcfs_init,
.init_sid = sctp_sched_fcfs_init_sid,
.free_sid = sctp_sched_fcfs_free_sid,
- .free = sctp_sched_fcfs_free,
.enqueue = sctp_sched_fcfs_enqueue,
.dequeue = sctp_sched_fcfs_dequeue,
.dequeue_done = sctp_sched_fcfs_dequeue_done,
@@ -131,6 +126,23 @@ void sctp_sched_ops_init(void)
sctp_sched_ops_rr_init();
}
+static void sctp_sched_free_sched(struct sctp_stream *stream)
+{
+ struct sctp_sched_ops *sched = sctp_sched_ops_from_stream(stream);
+ struct sctp_stream_out_ext *soute;
+ int i;
+
+ sched->unsched_all(stream);
+ for (i = 0; i < stream->outcnt; i++) {
+ soute = SCTP_SO(stream, i)->ext;
+ if (!soute)
+ continue;
+ sched->free_sid(stream, i);
+ /* Give the next scheduler a clean slate. */
+ memset_after(soute, 0, outq);
+ }
+}
+
int sctp_sched_set_sched(struct sctp_association *asoc,
enum sctp_sched_type sched)
{
@@ -146,18 +158,8 @@ int sctp_sched_set_sched(struct sctp_association *asoc,
if (sched > SCTP_SS_MAX)
return -EINVAL;
- if (old) {
- old->free(&asoc->stream);
-
- /* Give the next scheduler a clean slate. */
- for (i = 0; i < asoc->stream.outcnt; i++) {
- struct sctp_stream_out_ext *ext = SCTP_SO(&asoc->stream, i)->ext;
-
- if (!ext)
- continue;
- memset_after(ext, 0, outq);
- }
- }
+ if (old)
+ sctp_sched_free_sched(&asoc->stream);
asoc->outqueue.sched = n;
n->init(&asoc->stream);
@@ -181,7 +183,7 @@ int sctp_sched_set_sched(struct sctp_association *asoc,
return ret;
err:
- n->free(&asoc->stream);
+ sctp_sched_free_sched(&asoc->stream);
asoc->outqueue.sched = &sctp_sched_fcfs; /* Always safe */
return ret;
diff --git a/net/sctp/stream_sched_prio.c b/net/sctp/stream_sched_prio.c
index 4fc9f2923ed1..42d4800f263d 100644
--- a/net/sctp/stream_sched_prio.c
+++ b/net/sctp/stream_sched_prio.c
@@ -222,32 +222,6 @@ static void sctp_sched_prio_free_sid(struct sctp_stream *stream, __u16 sid)
kfree(prio);
}
-static void sctp_sched_prio_free(struct sctp_stream *stream)
-{
- struct sctp_stream_priorities *prio, *n;
- LIST_HEAD(list);
- int i;
-
- /* As we don't keep a list of priorities, to avoid multiple
- * frees we have to do it in 3 steps:
- * 1. unsched everyone, so the lists are free to use in 2.
- * 2. build the list of the priorities
- * 3. free the list
- */
- sctp_sched_prio_unsched_all(stream);
- for (i = 0; i < stream->outcnt; i++) {
- if (!SCTP_SO(stream, i)->ext)
- continue;
- prio = SCTP_SO(stream, i)->ext->prio_head;
- if (prio && list_empty(&prio->prio_sched))
- list_add(&prio->prio_sched, &list);
- }
- list_for_each_entry_safe(prio, n, &list, prio_sched) {
- list_del_init(&prio->prio_sched);
- kfree(prio);
- }
-}
-
static void sctp_sched_prio_enqueue(struct sctp_outq *q,
struct sctp_datamsg *msg)
{
@@ -342,7 +316,6 @@ static struct sctp_sched_ops sctp_sched_prio = {
.init = sctp_sched_prio_init,
.init_sid = sctp_sched_prio_init_sid,
.free_sid = sctp_sched_prio_free_sid,
- .free = sctp_sched_prio_free,
.enqueue = sctp_sched_prio_enqueue,
.dequeue = sctp_sched_prio_dequeue,
.dequeue_done = sctp_sched_prio_dequeue_done,
diff --git a/net/sctp/stream_sched_rr.c b/net/sctp/stream_sched_rr.c
index cc444fe0d67c..1f235e7f643a 100644
--- a/net/sctp/stream_sched_rr.c
+++ b/net/sctp/stream_sched_rr.c
@@ -94,11 +94,6 @@ static void sctp_sched_rr_free_sid(struct sctp_stream *stream, __u16 sid)
{
}
-static void sctp_sched_rr_free(struct sctp_stream *stream)
-{
- sctp_sched_rr_unsched_all(stream);
-}
-
static void sctp_sched_rr_enqueue(struct sctp_outq *q,
struct sctp_datamsg *msg)
{
@@ -182,7 +177,6 @@ static struct sctp_sched_ops sctp_sched_rr = {
.init = sctp_sched_rr_init,
.init_sid = sctp_sched_rr_init_sid,
.free_sid = sctp_sched_rr_free_sid,
- .free = sctp_sched_rr_free,
.enqueue = sctp_sched_rr_enqueue,
.dequeue = sctp_sched_rr_dequeue,
.dequeue_done = sctp_sched_rr_dequeue_done,
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 1ad0326ff4dc..33a82ecab9d5 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -3868,6 +3868,9 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flag
struct cfg80211_chan_def chandef = {};
int ret;
+ if (!link)
+ goto nla_put_failure;
+
if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id))
goto nla_put_failure;
if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN,
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index c3d950d29432..4f3f31244e8b 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -4311,8 +4311,10 @@ static int __init regulatory_init_db(void)
return -EINVAL;
err = load_builtin_regdb_keys();
- if (err)
+ if (err) {
+ platform_device_unregister(reg_pdev);
return err;
+ }
/* We always try to get an update for the static regdomain */
err = regulatory_hint_core(cfg80211_world_regdom->alpha2);
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index 26c74f5b473c..790bc31cf82e 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -158,9 +158,8 @@ static inline void bss_ref_put(struct cfg80211_registered_device *rdev,
if (bss->pub.hidden_beacon_bss) {
struct cfg80211_internal_bss *hbss;
- hbss = container_of(bss->pub.hidden_beacon_bss,
- struct cfg80211_internal_bss,
- pub);
+
+ hbss = bss_from_pub(bss->pub.hidden_beacon_bss);
hbss->refcount--;
if (hbss->refcount == 0)
bss_free(hbss);
@@ -169,9 +168,7 @@ static inline void bss_ref_put(struct cfg80211_registered_device *rdev,
if (bss->pub.transmitted_bss) {
struct cfg80211_internal_bss *tbss;
- tbss = container_of(bss->pub.transmitted_bss,
- struct cfg80211_internal_bss,
- pub);
+ tbss = bss_from_pub(bss->pub.transmitted_bss);
tbss->refcount--;
if (tbss->refcount == 0)
bss_free(tbss);
@@ -1290,7 +1287,8 @@ static int cmp_bss(struct cfg80211_bss *a,
int i, r;
if (a->channel != b->channel)
- return b->channel->center_freq - a->channel->center_freq;
+ return (b->channel->center_freq * 1000 + b->channel->freq_offset) -
+ (a->channel->center_freq * 1000 + a->channel->freq_offset);
a_ies = rcu_access_pointer(a->ies);
if (!a_ies)
@@ -1791,13 +1789,8 @@ cfg80211_bss_update(struct cfg80211_registered_device *rdev,
/* This must be before the call to bss_ref_get */
if (tmp->pub.transmitted_bss) {
- struct cfg80211_internal_bss *pbss =
- container_of(tmp->pub.transmitted_bss,
- struct cfg80211_internal_bss,
- pub);
-
new->pub.transmitted_bss = tmp->pub.transmitted_bss;
- bss_ref_get(rdev, pbss);
+ bss_ref_get(rdev, bss_from_pub(tmp->pub.transmitted_bss));
}
list_add_tail(&new->list, &rdev->bss_list);
@@ -2575,15 +2568,12 @@ EXPORT_SYMBOL(cfg80211_inform_bss_frame_data);
void cfg80211_ref_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
{
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
- struct cfg80211_internal_bss *bss;
if (!pub)
return;
- bss = container_of(pub, struct cfg80211_internal_bss, pub);
-
spin_lock_bh(&rdev->bss_lock);
- bss_ref_get(rdev, bss);
+ bss_ref_get(rdev, bss_from_pub(pub));
spin_unlock_bh(&rdev->bss_lock);
}
EXPORT_SYMBOL(cfg80211_ref_bss);
@@ -2591,15 +2581,12 @@ EXPORT_SYMBOL(cfg80211_ref_bss);
void cfg80211_put_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
{
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
- struct cfg80211_internal_bss *bss;
if (!pub)
return;
- bss = container_of(pub, struct cfg80211_internal_bss, pub);
-
spin_lock_bh(&rdev->bss_lock);
- bss_ref_put(rdev, bss);
+ bss_ref_put(rdev, bss_from_pub(pub));
spin_unlock_bh(&rdev->bss_lock);
}
EXPORT_SYMBOL(cfg80211_put_bss);
@@ -2613,7 +2600,7 @@ void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
if (WARN_ON(!pub))
return;
- bss = container_of(pub, struct cfg80211_internal_bss, pub);
+ bss = bss_from_pub(pub);
spin_lock_bh(&rdev->bss_lock);
if (list_empty(&bss->list))
@@ -2622,8 +2609,7 @@ void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
list_for_each_entry_safe(nontrans_bss, tmp,
&pub->nontrans_list,
nontrans_list) {
- tmp1 = container_of(nontrans_bss,
- struct cfg80211_internal_bss, pub);
+ tmp1 = bss_from_pub(nontrans_bss);
if (__cfg80211_unlink_bss(rdev, tmp1))
rdev->bss_generation++;
}
@@ -2680,9 +2666,7 @@ void cfg80211_update_assoc_bss_entry(struct wireless_dev *wdev,
/* use transmitting bss */
if (cbss->pub.transmitted_bss)
- cbss = container_of(cbss->pub.transmitted_bss,
- struct cfg80211_internal_bss,
- pub);
+ cbss = bss_from_pub(cbss->pub.transmitted_bss);
cbss->pub.channel = chan;
@@ -2711,8 +2695,7 @@ void cfg80211_update_assoc_bss_entry(struct wireless_dev *wdev,
list_for_each_entry_safe(nontrans_bss, tmp,
&new->pub.nontrans_list,
nontrans_list) {
- bss = container_of(nontrans_bss,
- struct cfg80211_internal_bss, pub);
+ bss = bss_from_pub(nontrans_bss);
if (__cfg80211_unlink_bss(rdev, bss))
rdev->bss_generation++;
}
@@ -2729,8 +2712,7 @@ void cfg80211_update_assoc_bss_entry(struct wireless_dev *wdev,
list_for_each_entry_safe(nontrans_bss, tmp,
&cbss->pub.nontrans_list,
nontrans_list) {
- bss = container_of(nontrans_bss,
- struct cfg80211_internal_bss, pub);
+ bss = bss_from_pub(nontrans_bss);
bss->pub.channel = chan;
rb_erase(&bss->rbn, &rdev->bss_tree);
rb_insert_bss(rdev, bss);
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
index f07aef7c592c..b57b091d8026 100644
--- a/tools/testing/selftests/Makefile
+++ b/tools/testing/selftests/Makefile
@@ -48,6 +48,7 @@ TARGETS += nci
TARGETS += net
TARGETS += net/af_unix
TARGETS += net/forwarding
+TARGETS += net/hsr
TARGETS += net/mptcp
TARGETS += net/openvswitch
TARGETS += netfilter
diff --git a/tools/testing/selftests/net/hsr/Makefile b/tools/testing/selftests/net/hsr/Makefile
new file mode 100644
index 000000000000..92c1d9d080cd
--- /dev/null
+++ b/tools/testing/selftests/net/hsr/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0
+
+top_srcdir = ../../../../..
+
+TEST_PROGS := hsr_ping.sh
+
+include ../../lib.mk
diff --git a/tools/testing/selftests/net/hsr/config b/tools/testing/selftests/net/hsr/config
new file mode 100644
index 000000000000..22061204fb69
--- /dev/null
+++ b/tools/testing/selftests/net/hsr/config
@@ -0,0 +1,4 @@
+CONFIG_IPV6=y
+CONFIG_NET_SCH_NETEM=m
+CONFIG_HSR=y
+CONFIG_VETH=y
diff --git a/tools/testing/selftests/net/hsr/hsr_ping.sh b/tools/testing/selftests/net/hsr/hsr_ping.sh
new file mode 100755
index 000000000000..df9143538708
--- /dev/null
+++ b/tools/testing/selftests/net/hsr/hsr_ping.sh
@@ -0,0 +1,256 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+ret=0
+ksft_skip=4
+ipv6=true
+
+optstring="h4"
+usage() {
+ echo "Usage: $0 [OPTION]"
+ echo -e "\t-4: IPv4 only: disable IPv6 tests (default: test both IPv4 and IPv6)"
+}
+
+while getopts "$optstring" option;do
+ case "$option" in
+ "h")
+ usage $0
+ exit 0
+ ;;
+ "4")
+ ipv6=false
+ ;;
+ "?")
+ usage $0
+ exit 1
+ ;;
+esac
+done
+
+sec=$(date +%s)
+rndh=$(printf %x $sec)-$(mktemp -u XXXXXX)
+ns1="ns1-$rndh"
+ns2="ns2-$rndh"
+ns3="ns3-$rndh"
+
+cleanup()
+{
+ local netns
+ for netns in "$ns1" "$ns2" "$ns3" ;do
+ ip netns del $netns
+ done
+}
+
+ip -Version > /dev/null 2>&1
+if [ $? -ne 0 ];then
+ echo "SKIP: Could not run test without ip tool"
+ exit $ksft_skip
+fi
+
+trap cleanup EXIT
+
+for i in "$ns1" "$ns2" "$ns3" ;do
+ ip netns add $i || exit $ksft_skip
+ ip -net $i link set lo up
+done
+
+echo "INFO: preparing interfaces."
+# Three HSR nodes. Each node has one link to each of its neighbour, two links in total.
+#
+# ns1eth1 ----- ns2eth1
+# hsr1 hsr2
+# ns1eth2 ns2eth2
+# | |
+# ns3eth1 ns3eth2
+# \ /
+# hsr3
+#
+# Interfaces
+ip link add ns1eth1 netns "$ns1" type veth peer name ns2eth1 netns "$ns2"
+ip link add ns1eth2 netns "$ns1" type veth peer name ns3eth1 netns "$ns3"
+ip link add ns3eth2 netns "$ns3" type veth peer name ns2eth2 netns "$ns2"
+
+# HSRv0.
+ip -net "$ns1" link add name hsr1 type hsr slave1 ns1eth1 slave2 ns1eth2 supervision 45 version 0 proto 0
+ip -net "$ns2" link add name hsr2 type hsr slave1 ns2eth1 slave2 ns2eth2 supervision 45 version 0 proto 0
+ip -net "$ns3" link add name hsr3 type hsr slave1 ns3eth1 slave2 ns3eth2 supervision 45 version 0 proto 0
+
+# IP for HSR
+ip -net "$ns1" addr add 100.64.0.1/24 dev hsr1
+ip -net "$ns1" addr add dead:beef:1::1/64 dev hsr1 nodad
+ip -net "$ns2" addr add 100.64.0.2/24 dev hsr2
+ip -net "$ns2" addr add dead:beef:1::2/64 dev hsr2 nodad
+ip -net "$ns3" addr add 100.64.0.3/24 dev hsr3
+ip -net "$ns3" addr add dead:beef:1::3/64 dev hsr3 nodad
+
+# All Links up
+ip -net "$ns1" link set ns1eth1 up
+ip -net "$ns1" link set ns1eth2 up
+ip -net "$ns1" link set hsr1 up
+
+ip -net "$ns2" link set ns2eth1 up
+ip -net "$ns2" link set ns2eth2 up
+ip -net "$ns2" link set hsr2 up
+
+ip -net "$ns3" link set ns3eth1 up
+ip -net "$ns3" link set ns3eth2 up
+ip -net "$ns3" link set hsr3 up
+
+# $1: IP address
+is_v6()
+{
+ [ -z "${1##*:*}" ]
+}
+
+do_ping()
+{
+ local netns="$1"
+ local connect_addr="$2"
+ local ping_args="-q -c 2"
+
+ if is_v6 "${connect_addr}"; then
+ $ipv6 || return 0
+ ping_args="${ping_args} -6"
+ fi
+
+ ip netns exec ${netns} ping ${ping_args} $connect_addr >/dev/null
+ if [ $? -ne 0 ] ; then
+ echo "$netns -> $connect_addr connectivity [ FAIL ]" 1>&2
+ ret=1
+ return 1
+ fi
+
+ return 0
+}
+
+do_ping_long()
+{
+ local netns="$1"
+ local connect_addr="$2"
+ local ping_args="-q -c 10"
+
+ if is_v6 "${connect_addr}"; then
+ $ipv6 || return 0
+ ping_args="${ping_args} -6"
+ fi
+
+ OUT="$(LANG=C ip netns exec ${netns} ping ${ping_args} $connect_addr | grep received)"
+ if [ $? -ne 0 ] ; then
+ echo "$netns -> $connect_addr ping [ FAIL ]" 1>&2
+ ret=1
+ return 1
+ fi
+
+ VAL="$(echo $OUT | cut -d' ' -f1-8)"
+ if [ "$VAL" != "10 packets transmitted, 10 received, 0% packet loss," ]
+ then
+ echo "$netns -> $connect_addr ping TEST [ FAIL ]"
+ echo "Expect to send and receive 10 packets and no duplicates."
+ echo "Full message: ${OUT}."
+ ret=1
+ return 1
+ fi
+
+ return 0
+}
+
+stop_if_error()
+{
+ local msg="$1"
+
+ if [ ${ret} -ne 0 ]; then
+ echo "FAIL: ${msg}" 1>&2
+ exit ${ret}
+ fi
+}
+
+
+echo "INFO: Initial validation ping."
+# Each node has to be able each one.
+do_ping "$ns1" 100.64.0.2
+do_ping "$ns2" 100.64.0.1
+do_ping "$ns3" 100.64.0.1
+stop_if_error "Initial validation failed."
+
+do_ping "$ns1" 100.64.0.3
+do_ping "$ns2" 100.64.0.3
+do_ping "$ns3" 100.64.0.2
+
+do_ping "$ns1" dead:beef:1::2
+do_ping "$ns1" dead:beef:1::3
+do_ping "$ns2" dead:beef:1::1
+do_ping "$ns2" dead:beef:1::2
+do_ping "$ns3" dead:beef:1::1
+do_ping "$ns3" dead:beef:1::2
+
+stop_if_error "Initial validation failed."
+
+# Wait until supervisor all supervision frames have been processed and the node
+# entries have been merged. Otherwise duplicate frames will be observed which is
+# valid at this stage.
+WAIT=5
+while [ ${WAIT} -gt 0 ]
+do
+ grep 00:00:00:00:00:00 /sys/kernel/debug/hsr/hsr*/node_table
+ if [ $? -ne 0 ]
+ then
+ break
+ fi
+ sleep 1
+ let WAIT = WAIT - 1
+done
+
+# Just a safety delay in case the above check didn't handle it.
+sleep 1
+
+echo "INFO: Longer ping test."
+do_ping_long "$ns1" 100.64.0.2
+do_ping_long "$ns1" dead:beef:1::2
+do_ping_long "$ns1" 100.64.0.3
+do_ping_long "$ns1" dead:beef:1::3
+
+stop_if_error "Longer ping test failed."
+
+do_ping_long "$ns2" 100.64.0.1
+do_ping_long "$ns2" dead:beef:1::1
+do_ping_long "$ns2" 100.64.0.3
+do_ping_long "$ns2" dead:beef:1::2
+stop_if_error "Longer ping test failed."
+
+do_ping_long "$ns3" 100.64.0.1
+do_ping_long "$ns3" dead:beef:1::1
+do_ping_long "$ns3" 100.64.0.2
+do_ping_long "$ns3" dead:beef:1::2
+stop_if_error "Longer ping test failed."
+
+echo "INFO: Cutting one link."
+do_ping_long "$ns1" 100.64.0.3 &
+
+sleep 3
+ip -net "$ns3" link set ns3eth1 down
+wait
+
+ip -net "$ns3" link set ns3eth1 up
+
+stop_if_error "Failed with one link down."
+
+echo "INFO: Delay the link and drop a few packages."
+tc -net "$ns3" qdisc add dev ns3eth1 root netem delay 50ms
+tc -net "$ns2" qdisc add dev ns2eth1 root netem delay 5ms loss 25%
+
+do_ping_long "$ns1" 100.64.0.2
+do_ping_long "$ns1" 100.64.0.3
+
+stop_if_error "Failed with delay and packetloss."
+
+do_ping_long "$ns2" 100.64.0.1
+do_ping_long "$ns2" 100.64.0.3
+
+stop_if_error "Failed with delay and packetloss."
+
+do_ping_long "$ns3" 100.64.0.1
+do_ping_long "$ns3" 100.64.0.2
+stop_if_error "Failed with delay and packetloss."
+
+echo "INFO: All good."
+exit $ret
diff --git a/tools/testing/selftests/net/mptcp/diag.sh b/tools/testing/selftests/net/mptcp/diag.sh
index 515859a5168b..24bcd7b9bdb2 100755
--- a/tools/testing/selftests/net/mptcp/diag.sh
+++ b/tools/testing/selftests/net/mptcp/diag.sh
@@ -1,6 +1,7 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
+sec=$(date +%s)
rndh=$(printf %x $sec)-$(mktemp -u XXXXXX)
ns="ns1-$rndh"
ksft_skip=4
diff --git a/tools/testing/selftests/net/mptcp/mptcp_connect.sh b/tools/testing/selftests/net/mptcp/mptcp_connect.sh
index 60198b91a530..a43d3e2f59bb 100755
--- a/tools/testing/selftests/net/mptcp/mptcp_connect.sh
+++ b/tools/testing/selftests/net/mptcp/mptcp_connect.sh
@@ -274,8 +274,7 @@ check_transfer()
check_mptcp_disabled()
{
- local disabled_ns
- disabled_ns="ns_disabled-$sech-$(mktemp -u XXXXXX)"
+ local disabled_ns="ns_disabled-$rndh"
ip netns add ${disabled_ns} || exit $ksft_skip
# net.mptcp.enabled should be enabled by default
@@ -781,7 +780,6 @@ run_tests_mptfo()
run_tests_disconnect()
{
- local peekmode="$1"
local old_cin=$cin
local old_sin=$sin
@@ -789,7 +787,6 @@ run_tests_disconnect()
# force do_transfer to cope with the multiple tranmissions
sin="$cin.disconnect"
- sin_disconnect=$old_sin
cin="$cin.disconnect"
cin_disconnect="$old_cin"
connect_per_transfer=3
@@ -800,7 +797,6 @@ run_tests_disconnect()
# restore previous status
sin=$old_sin
- sin_disconnect="$cout".disconnect
cin=$old_cin
cin_disconnect="$cin".disconnect
connect_per_transfer=1
diff --git a/tools/testing/selftests/net/mptcp/mptcp_join.sh b/tools/testing/selftests/net/mptcp/mptcp_join.sh
index 2eeaf4aca644..d11d3d566608 100755
--- a/tools/testing/selftests/net/mptcp/mptcp_join.sh
+++ b/tools/testing/selftests/net/mptcp/mptcp_join.sh
@@ -26,6 +26,10 @@ ip_mptcp=0
check_invert=0
validate_checksum=0
init=0
+evts_ns1=""
+evts_ns2=""
+evts_ns1_pid=0
+evts_ns2_pid=0
declare -A all_tests
declare -a only_tests_ids
@@ -59,8 +63,9 @@ init_partial()
{
capout=$(mktemp)
- local rndh
- rndh=$(mktemp -u XXXXXX)
+ local sec rndh
+ sec=$(date +%s)
+ rndh=$(printf %x $sec)-$(mktemp -u XXXXXX)
ns1="ns1-$rndh"
ns2="ns2-$rndh"
@@ -153,6 +158,8 @@ init() {
cin=$(mktemp)
cinsent=$(mktemp)
cout=$(mktemp)
+ evts_ns1=$(mktemp)
+ evts_ns2=$(mktemp)
trap cleanup EXIT
@@ -164,6 +171,7 @@ cleanup()
{
rm -f "$cin" "$cout" "$sinfail"
rm -f "$sin" "$sout" "$cinsent" "$cinfail"
+ rm -rf $evts_ns1 $evts_ns2
cleanup_partial
}
@@ -319,6 +327,18 @@ reset_with_fail()
index 100 || exit 1
}
+reset_with_events()
+{
+ reset "${1}" || return 1
+
+ :> "$evts_ns1"
+ :> "$evts_ns2"
+ ip netns exec $ns1 ./pm_nl_ctl events >> "$evts_ns1" 2>&1 &
+ evts_ns1_pid=$!
+ ip netns exec $ns2 ./pm_nl_ctl events >> "$evts_ns2" 2>&1 &
+ evts_ns2_pid=$!
+}
+
fail_test()
{
ret=1
@@ -472,6 +492,12 @@ kill_wait()
wait $1 2>/dev/null
}
+kill_events_pids()
+{
+ kill_wait $evts_ns1_pid
+ kill_wait $evts_ns2_pid
+}
+
pm_nl_set_limits()
{
local ns=$1
@@ -672,10 +698,6 @@ do_transfer()
local port=$((10000 + TEST_COUNT - 1))
local cappid
local userspace_pm=0
- local evts_ns1
- local evts_ns1_pid
- local evts_ns2
- local evts_ns2_pid
:> "$cout"
:> "$sout"
@@ -752,17 +774,6 @@ do_transfer()
addr_nr_ns2=${addr_nr_ns2:9}
fi
- if [ $userspace_pm -eq 1 ]; then
- evts_ns1=$(mktemp)
- evts_ns2=$(mktemp)
- :> "$evts_ns1"
- :> "$evts_ns2"
- ip netns exec ${listener_ns} ./pm_nl_ctl events >> "$evts_ns1" 2>&1 &
- evts_ns1_pid=$!
- ip netns exec ${connector_ns} ./pm_nl_ctl events >> "$evts_ns2" 2>&1 &
- evts_ns2_pid=$!
- fi
-
local local_addr
if is_v6 "${connect_addr}"; then
local_addr="::"
@@ -829,7 +840,8 @@ do_transfer()
if [ $userspace_pm -eq 0 ]; then
pm_nl_add_endpoint $ns1 $addr flags signal
else
- tk=$(sed -n 's/.*\(token:\)\([[:digit:]]*\).*$/\2/p;q' "$evts_ns1")
+ tk=$(grep "type:1," "$evts_ns1" |
+ sed -n 's/.*\(token:\)\([[:digit:]]*\).*$/\2/p;q')
ip netns exec ${listener_ns} ./pm_nl_ctl ann $addr token $tk id $id
sleep 1
ip netns exec ${listener_ns} ./pm_nl_ctl rem token $tk id $id
@@ -980,12 +992,6 @@ do_transfer()
kill $cappid
fi
- if [ $userspace_pm -eq 1 ]; then
- kill_wait $evts_ns1_pid
- kill_wait $evts_ns2_pid
- rm -rf $evts_ns1 $evts_ns2
- fi
-
NSTAT_HISTORY=/tmp/${listener_ns}.nstat ip netns exec ${listener_ns} \
nstat | grep Tcp > /tmp/${listener_ns}.out
NSTAT_HISTORY=/tmp/${connector_ns}.nstat ip netns exec ${connector_ns} \
@@ -2507,6 +2513,57 @@ backup_tests()
fi
}
+LISTENER_CREATED=15 #MPTCP_EVENT_LISTENER_CREATED
+LISTENER_CLOSED=16 #MPTCP_EVENT_LISTENER_CLOSED
+
+AF_INET=2
+AF_INET6=10
+
+verify_listener_events()
+{
+ local evt=$1
+ local e_type=$2
+ local e_family=$3
+ local e_saddr=$4
+ local e_sport=$5
+ local type
+ local family
+ local saddr
+ local sport
+
+ if [ $e_type = $LISTENER_CREATED ]; then
+ stdbuf -o0 -e0 printf "\t\t\t\t\t CREATE_LISTENER %s:%s"\
+ $e_saddr $e_sport
+ elif [ $e_type = $LISTENER_CLOSED ]; then
+ stdbuf -o0 -e0 printf "\t\t\t\t\t CLOSE_LISTENER %s:%s "\
+ $e_saddr $e_sport
+ fi
+
+ type=$(grep "type:$e_type," $evt |
+ sed --unbuffered -n 's/.*\(type:\)\([[:digit:]]*\).*$/\2/p;q')
+ family=$(grep "type:$e_type," $evt |
+ sed --unbuffered -n 's/.*\(family:\)\([[:digit:]]*\).*$/\2/p;q')
+ sport=$(grep "type:$e_type," $evt |
+ sed --unbuffered -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q')
+ if [ $family ] && [ $family = $AF_INET6 ]; then
+ saddr=$(grep "type:$e_type," $evt |
+ sed --unbuffered -n 's/.*\(saddr6:\)\([0-9a-f:.]*\).*$/\2/p;q')
+ else
+ saddr=$(grep "type:$e_type," $evt |
+ sed --unbuffered -n 's/.*\(saddr4:\)\([0-9.]*\).*$/\2/p;q')
+ fi
+
+ if [ $type ] && [ $type = $e_type ] &&
+ [ $family ] && [ $family = $e_family ] &&
+ [ $saddr ] && [ $saddr = $e_saddr ] &&
+ [ $sport ] && [ $sport = $e_sport ]; then
+ stdbuf -o0 -e0 printf "[ ok ]\n"
+ return 0
+ fi
+ fail_test
+ stdbuf -o0 -e0 printf "[fail]\n"
+}
+
add_addr_ports_tests()
{
# signal address with port
@@ -2531,7 +2588,8 @@ add_addr_ports_tests()
fi
# single address with port, remove
- if reset "remove single address with port"; then
+ # pm listener events
+ if reset_with_events "remove single address with port"; then
pm_nl_set_limits $ns1 0 1
pm_nl_add_endpoint $ns1 10.0.2.1 flags signal port 10100
pm_nl_set_limits $ns2 1 1
@@ -2539,6 +2597,10 @@ add_addr_ports_tests()
chk_join_nr 1 1 1
chk_add_nr 1 1 1
chk_rm_nr 1 1 invert
+
+ verify_listener_events $evts_ns1 $LISTENER_CREATED $AF_INET 10.0.2.1 10100
+ verify_listener_events $evts_ns1 $LISTENER_CLOSED $AF_INET 10.0.2.1 10100
+ kill_events_pids
fi
# subflow and signal with port, remove
@@ -2959,22 +3021,24 @@ userspace_tests()
fi
# userspace pm add & remove address
- if reset "userspace pm add & remove address"; then
+ if reset_with_events "userspace pm add & remove address"; then
set_userspace_pm $ns1
pm_nl_set_limits $ns2 1 1
run_tests $ns1 $ns2 10.0.1.1 0 userspace_1 0 slow
chk_join_nr 1 1 1
chk_add_nr 1 1
chk_rm_nr 1 1 invert
+ kill_events_pids
fi
# userspace pm create destroy subflow
- if reset "userspace pm create destroy subflow"; then
+ if reset_with_events "userspace pm create destroy subflow"; then
set_userspace_pm $ns2
pm_nl_set_limits $ns1 0 1
run_tests $ns1 $ns2 10.0.1.1 0 0 userspace_1 slow
chk_join_nr 1 1 1
chk_rm_nr 0 1
+ kill_events_pids
fi
}
diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh b/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh
index 80d36f7cfee8..1b70c0a304ce 100755
--- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh
+++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh
@@ -10,13 +10,19 @@ ksft_skip=4
timeout_poll=30
timeout_test=$((timeout_poll * 2 + 1))
mptcp_connect=""
-do_all_tests=1
+
+sec=$(date +%s)
+rndh=$(printf %x $sec)-$(mktemp -u XXXXXX)
+ns1="ns1-$rndh"
+ns2="ns2-$rndh"
+ns_sbox="ns_sbox-$rndh"
add_mark_rules()
{
local ns=$1
local m=$2
+ local t
for t in iptables ip6tables; do
# just to debug: check we have multiple subflows connection requests
ip netns exec $ns $t -A OUTPUT -p tcp --syn -m mark --mark $m -j ACCEPT
@@ -31,12 +37,7 @@ add_mark_rules()
init()
{
- rndh=$(printf %x $sec)-$(mktemp -u XXXXXX)
-
- ns1="ns1-$rndh"
- ns2="ns2-$rndh"
- ns_sbox="ns_sbox-$rndh"
-
+ local netns
for netns in "$ns1" "$ns2" "$ns_sbox";do
ip netns add $netns || exit $ksft_skip
ip -net $netns link set lo up
@@ -45,6 +46,7 @@ init()
ip netns exec $netns sysctl -q net.ipv4.conf.default.rp_filter=0
done
+ local i
for i in `seq 1 4`; do
ip link add ns1eth$i netns "$ns1" type veth peer name ns2eth$i netns "$ns2"
ip -net "$ns1" addr add 10.0.$i.1/24 dev ns1eth$i
@@ -74,6 +76,7 @@ init()
cleanup()
{
+ local netns
for netns in "$ns1" "$ns2" "$ns_sbox"; do
ip netns del $netns
done
@@ -104,15 +107,17 @@ check_mark()
local ns=$1
local af=$2
- tables=iptables
+ local tables=iptables
if [ $af -eq 6 ];then
tables=ip6tables
fi
+ local counters values
counters=$(ip netns exec $ns $tables -v -L OUTPUT | grep DROP)
values=${counters%DROP*}
+ local v
for v in $values; do
if [ $v -ne 0 ]; then
echo "FAIL: got $tables $values in ns $ns , not 0 - not all expected packets marked" 1>&2
@@ -132,9 +137,9 @@ print_file_err()
check_transfer()
{
- in=$1
- out=$2
- what=$3
+ local in=$1
+ local out=$2
+ local what=$3
cmp "$in" "$out" > /dev/null 2>&1
if [ $? -ne 0 ] ;then
@@ -157,18 +162,18 @@ is_v6()
do_transfer()
{
- listener_ns="$1"
- connector_ns="$2"
- cl_proto="$3"
- srv_proto="$4"
- connect_addr="$5"
+ local listener_ns="$1"
+ local connector_ns="$2"
+ local cl_proto="$3"
+ local srv_proto="$4"
+ local connect_addr="$5"
- port=12001
+ local port=12001
:> "$cout"
:> "$sout"
- mptcp_connect="./mptcp_connect -r 20"
+ local mptcp_connect="./mptcp_connect -r 20"
local local_addr
if is_v6 "${connect_addr}"; then
@@ -181,7 +186,7 @@ do_transfer()
ip netns exec ${listener_ns} \
$mptcp_connect -t ${timeout_poll} -l -M 1 -p $port -s ${srv_proto} -c TIMESTAMPNS,TCPINQ \
${local_addr} < "$sin" > "$sout" &
- spid=$!
+ local spid=$!
sleep 1
@@ -190,12 +195,12 @@ do_transfer()
$mptcp_connect -t ${timeout_poll} -M 2 -p $port -s ${cl_proto} -c TIMESTAMPNS,TCPINQ \
$connect_addr < "$cin" > "$cout" &
- cpid=$!
+ local cpid=$!
wait $cpid
- retc=$?
+ local retc=$?
wait $spid
- rets=$?
+ local rets=$?
if [ ${rets} -ne 0 ] || [ ${retc} -ne 0 ]; then
echo " client exit code $retc, server $rets" 1>&2
@@ -230,9 +235,9 @@ do_transfer()
make_file()
{
- name=$1
- who=$2
- size=$3
+ local name=$1
+ local who=$2
+ local size=$3
dd if=/dev/urandom of="$name" bs=1024 count=$size 2> /dev/null
echo -e "\nMPTCP_TEST_FILE_END_MARKER" >> "$name"
@@ -265,9 +270,9 @@ do_mptcp_sockopt_tests()
run_tests()
{
- listener_ns="$1"
- connector_ns="$2"
- connect_addr="$3"
+ local listener_ns="$1"
+ local connector_ns="$2"
+ local connect_addr="$3"
local lret=0
do_transfer ${listener_ns} ${connector_ns} MPTCP MPTCP ${connect_addr}
@@ -282,8 +287,8 @@ run_tests()
do_tcpinq_test()
{
- ip netns exec "$ns1" ./mptcp_inq "$@"
- lret=$?
+ ip netns exec "$ns_sbox" ./mptcp_inq "$@"
+ local lret=$?
if [ $lret -ne 0 ];then
ret=$lret
echo "FAIL: mptcp_inq $@" 1>&2
@@ -298,9 +303,7 @@ do_tcpinq_tests()
{
local lret=0
- ip netns exec "$ns1" iptables -F
- ip netns exec "$ns1" ip6tables -F
-
+ local args
for args in "-t tcp" "-r tcp"; do
do_tcpinq_test $args
lret=$?
diff --git a/tools/testing/selftests/net/mptcp/simult_flows.sh b/tools/testing/selftests/net/mptcp/simult_flows.sh
index a5aeefd58ab3..9f22f7e5027d 100755
--- a/tools/testing/selftests/net/mptcp/simult_flows.sh
+++ b/tools/testing/selftests/net/mptcp/simult_flows.sh
@@ -1,6 +1,7 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
+sec=$(date +%s)
rndh=$(printf %x $sec)-$(mktemp -u XXXXXX)
ns1="ns1-$rndh"
ns2="ns2-$rndh"
@@ -148,9 +149,6 @@ do_transfer()
:> "$sout"
:> "$capout"
- local addr_port
- addr_port=$(printf "%s:%d" ${connect_addr} ${port})
-
if $capture; then
local capuser
if [ -z $SUDO_USER ] ; then
diff --git a/tools/testing/selftests/net/mptcp/userspace_pm.sh b/tools/testing/selftests/net/mptcp/userspace_pm.sh
index 3229725b64b0..a29deb9fa024 100755
--- a/tools/testing/selftests/net/mptcp/userspace_pm.sh
+++ b/tools/testing/selftests/net/mptcp/userspace_pm.sh
@@ -11,11 +11,17 @@ ANNOUNCED=6 # MPTCP_EVENT_ANNOUNCED
REMOVED=7 # MPTCP_EVENT_REMOVED
SUB_ESTABLISHED=10 # MPTCP_EVENT_SUB_ESTABLISHED
SUB_CLOSED=11 # MPTCP_EVENT_SUB_CLOSED
+LISTENER_CREATED=15 #MPTCP_EVENT_LISTENER_CREATED
+LISTENER_CLOSED=16 #MPTCP_EVENT_LISTENER_CLOSED
AF_INET=2
AF_INET6=10
-evts_pid=0
+file=""
+server_evts=""
+client_evts=""
+server_evts_pid=0
+client_evts_pid=0
client4_pid=0
server4_pid=0
client6_pid=0
@@ -33,7 +39,7 @@ client_addr_id=${RANDOM:0:2}
server_addr_id=${RANDOM:0:2}
sec=$(date +%s)
-rndh=$(stdbuf -o0 -e0 printf %x "$sec")-$(mktemp -u XXXXXX)
+rndh=$(printf %x "$sec")-$(mktemp -u XXXXXX)
ns1="ns1-$rndh"
ns2="ns2-$rndh"
@@ -47,7 +53,7 @@ cleanup()
{
echo "cleanup"
- rm -rf $file
+ rm -rf $file $client_evts $server_evts
# Terminate the MPTCP connection and related processes
if [ $client4_pid -ne 0 ]; then
@@ -62,8 +68,11 @@ cleanup()
if [ $server6_pid -ne 0 ]; then
kill_wait $server6_pid
fi
- if [ $evts_pid -ne 0 ]; then
- kill_wait $evts_pid
+ if [ $server_evts_pid -ne 0 ]; then
+ kill_wait $server_evts_pid
+ fi
+ if [ $client_evts_pid -ne 0 ]; then
+ kill_wait $client_evts_pid
fi
local netns
for netns in "$ns1" "$ns2" ;do
@@ -113,8 +122,9 @@ make_file()
make_connection()
{
- local file
- file=$(mktemp)
+ if [ -z "$file" ]; then
+ file=$(mktemp)
+ fi
make_file "$file" "client"
local is_v6=$1
@@ -132,16 +142,24 @@ make_connection()
# Capture netlink events over the two network namespaces running
# the MPTCP client and server
- local client_evts
- client_evts=$(mktemp)
+ if [ -z "$client_evts" ]; then
+ client_evts=$(mktemp)
+ fi
:>"$client_evts"
+ if [ $client_evts_pid -ne 0 ]; then
+ kill_wait $client_evts_pid
+ fi
ip netns exec "$ns2" ./pm_nl_ctl events >> "$client_evts" 2>&1 &
- local client_evts_pid=$!
- local server_evts
- server_evts=$(mktemp)
+ client_evts_pid=$!
+ if [ -z "$server_evts" ]; then
+ server_evts=$(mktemp)
+ fi
:>"$server_evts"
+ if [ $server_evts_pid -ne 0 ]; then
+ kill_wait $server_evts_pid
+ fi
ip netns exec "$ns1" ./pm_nl_ctl events >> "$server_evts" 2>&1 &
- local server_evts_pid=$!
+ server_evts_pid=$!
sleep 0.5
# Run the server
@@ -159,7 +177,6 @@ make_connection()
sleep 1
# Capture client/server attributes from MPTCP connection netlink events
- kill_wait $client_evts_pid
local client_token
local client_port
@@ -171,11 +188,10 @@ make_connection()
client_port=$(sed --unbuffered -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$client_evts")
client_serverside=$(sed --unbuffered -n 's/.*\(server_side:\)\([[:digit:]]*\).*$/\2/p;q'\
"$client_evts")
- kill_wait $server_evts_pid
- server_token=$(sed --unbuffered -n 's/.*\(token:\)\([[:digit:]]*\).*$/\2/p;q' "$server_evts")
- server_serverside=$(sed --unbuffered -n 's/.*\(server_side:\)\([[:digit:]]*\).*$/\2/p;q'\
- "$server_evts")
- rm -f "$client_evts" "$server_evts" "$file"
+ server_token=$(grep "type:1," "$server_evts" |
+ sed --unbuffered -n 's/.*\(token:\)\([[:digit:]]*\).*$/\2/p;q')
+ server_serverside=$(grep "type:1," "$server_evts" |
+ sed --unbuffered -n 's/.*\(server_side:\)\([[:digit:]]*\).*$/\2/p;q')
if [ "$client_token" != "" ] && [ "$server_token" != "" ] && [ "$client_serverside" = 0 ] &&
[ "$server_serverside" = 1 ]
@@ -239,13 +255,8 @@ verify_announce_event()
test_announce()
{
- local evts
- evts=$(mktemp)
# Capture events on the network namespace running the server
- :>"$evts"
- ip netns exec "$ns1" ./pm_nl_ctl events >> "$evts" 2>&1 &
- evts_pid=$!
- sleep 0.5
+ :>"$server_evts"
# ADD_ADDR using an invalid token should result in no action
local invalid_token=$(( client4_token - 1))
@@ -253,7 +264,7 @@ test_announce()
$client_addr_id dev ns2eth1 > /dev/null 2>&1
local type
- type=$(sed --unbuffered -n 's/.*\(type:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
+ type=$(sed --unbuffered -n 's/.*\(type:\)\([[:digit:]]*\).*$/\2/p;q' "$server_evts")
stdbuf -o0 -e0 printf "ADD_ADDR 10.0.2.2 (ns2) => ns1, invalid token \t\t"
if [ "$type" = "" ]
then
@@ -264,71 +275,63 @@ test_announce()
fi
# ADD_ADDR from the client to server machine reusing the subflow port
- :>"$evts"
+ :>"$server_evts"
ip netns exec "$ns2"\
./pm_nl_ctl ann 10.0.2.2 token "$client4_token" id $client_addr_id dev\
ns2eth1 > /dev/null 2>&1
stdbuf -o0 -e0 printf "ADD_ADDR id:%d 10.0.2.2 (ns2) => ns1, reuse port \t\t" $client_addr_id
sleep 0.5
- verify_announce_event "$evts" "$ANNOUNCED" "$server4_token" "10.0.2.2" "$client_addr_id"\
+ verify_announce_event $server_evts $ANNOUNCED $server4_token "10.0.2.2" $client_addr_id \
"$client4_port"
# ADD_ADDR6 from the client to server machine reusing the subflow port
- :>"$evts"
+ :>"$server_evts"
ip netns exec "$ns2" ./pm_nl_ctl ann\
dead:beef:2::2 token "$client6_token" id $client_addr_id dev ns2eth1 > /dev/null 2>&1
stdbuf -o0 -e0 printf "ADD_ADDR6 id:%d dead:beef:2::2 (ns2) => ns1, reuse port\t\t" $client_addr_id
sleep 0.5
- verify_announce_event "$evts" "$ANNOUNCED" "$server6_token" "dead:beef:2::2"\
+ verify_announce_event "$server_evts" "$ANNOUNCED" "$server6_token" "dead:beef:2::2"\
"$client_addr_id" "$client6_port" "v6"
# ADD_ADDR from the client to server machine using a new port
- :>"$evts"
+ :>"$server_evts"
client_addr_id=$((client_addr_id+1))
ip netns exec "$ns2" ./pm_nl_ctl ann 10.0.2.2 token "$client4_token" id\
$client_addr_id dev ns2eth1 port $new4_port > /dev/null 2>&1
stdbuf -o0 -e0 printf "ADD_ADDR id:%d 10.0.2.2 (ns2) => ns1, new port \t\t\t" $client_addr_id
sleep 0.5
- verify_announce_event "$evts" "$ANNOUNCED" "$server4_token" "10.0.2.2"\
+ verify_announce_event "$server_evts" "$ANNOUNCED" "$server4_token" "10.0.2.2"\
"$client_addr_id" "$new4_port"
- kill_wait $evts_pid
-
# Capture events on the network namespace running the client
- :>"$evts"
- ip netns exec "$ns2" ./pm_nl_ctl events >> "$evts" 2>&1 &
- evts_pid=$!
- sleep 0.5
+ :>"$client_evts"
# ADD_ADDR from the server to client machine reusing the subflow port
ip netns exec "$ns1" ./pm_nl_ctl ann 10.0.2.1 token "$server4_token" id\
$server_addr_id dev ns1eth2 > /dev/null 2>&1
stdbuf -o0 -e0 printf "ADD_ADDR id:%d 10.0.2.1 (ns1) => ns2, reuse port \t\t" $server_addr_id
sleep 0.5
- verify_announce_event "$evts" "$ANNOUNCED" "$client4_token" "10.0.2.1"\
+ verify_announce_event "$client_evts" "$ANNOUNCED" "$client4_token" "10.0.2.1"\
"$server_addr_id" "$app4_port"
# ADD_ADDR6 from the server to client machine reusing the subflow port
- :>"$evts"
+ :>"$client_evts"
ip netns exec "$ns1" ./pm_nl_ctl ann dead:beef:2::1 token "$server6_token" id\
$server_addr_id dev ns1eth2 > /dev/null 2>&1
stdbuf -o0 -e0 printf "ADD_ADDR6 id:%d dead:beef:2::1 (ns1) => ns2, reuse port\t\t" $server_addr_id
sleep 0.5
- verify_announce_event "$evts" "$ANNOUNCED" "$client6_token" "dead:beef:2::1"\
+ verify_announce_event "$client_evts" "$ANNOUNCED" "$client6_token" "dead:beef:2::1"\
"$server_addr_id" "$app6_port" "v6"
# ADD_ADDR from the server to client machine using a new port
- :>"$evts"
+ :>"$client_evts"
server_addr_id=$((server_addr_id+1))
ip netns exec "$ns1" ./pm_nl_ctl ann 10.0.2.1 token "$server4_token" id\
$server_addr_id dev ns1eth2 port $new4_port > /dev/null 2>&1
stdbuf -o0 -e0 printf "ADD_ADDR id:%d 10.0.2.1 (ns1) => ns2, new port \t\t\t" $server_addr_id
sleep 0.5
- verify_announce_event "$evts" "$ANNOUNCED" "$client4_token" "10.0.2.1"\
+ verify_announce_event "$client_evts" "$ANNOUNCED" "$client4_token" "10.0.2.1"\
"$server_addr_id" "$new4_port"
-
- kill_wait $evts_pid
- rm -f "$evts"
}
verify_remove_event()
@@ -356,14 +359,8 @@ verify_remove_event()
test_remove()
{
- local evts
- evts=$(mktemp)
-
# Capture events on the network namespace running the server
- :>"$evts"
- ip netns exec "$ns1" ./pm_nl_ctl events >> "$evts" 2>&1 &
- evts_pid=$!
- sleep 0.5
+ :>"$server_evts"
# RM_ADDR using an invalid token should result in no action
local invalid_token=$(( client4_token - 1 ))
@@ -372,7 +369,7 @@ test_remove()
stdbuf -o0 -e0 printf "RM_ADDR id:%d ns2 => ns1, invalid token \t"\
$client_addr_id
local type
- type=$(sed --unbuffered -n 's/.*\(type:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
+ type=$(sed --unbuffered -n 's/.*\(type:\)\([[:digit:]]*\).*$/\2/p;q' "$server_evts")
if [ "$type" = "" ]
then
stdbuf -o0 -e0 printf "[OK]\n"
@@ -386,7 +383,7 @@ test_remove()
$invalid_id > /dev/null 2>&1
stdbuf -o0 -e0 printf "RM_ADDR id:%d ns2 => ns1, invalid id \t"\
$invalid_id
- type=$(sed --unbuffered -n 's/.*\(type:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
+ type=$(sed --unbuffered -n 's/.*\(type:\)\([[:digit:]]*\).*$/\2/p;q' "$server_evts")
if [ "$type" = "" ]
then
stdbuf -o0 -e0 printf "[OK]\n"
@@ -395,40 +392,35 @@ test_remove()
fi
# RM_ADDR from the client to server machine
- :>"$evts"
+ :>"$server_evts"
ip netns exec "$ns2" ./pm_nl_ctl rem token "$client4_token" id\
$client_addr_id > /dev/null 2>&1
stdbuf -o0 -e0 printf "RM_ADDR id:%d ns2 => ns1 \t"\
$client_addr_id
sleep 0.5
- verify_remove_event "$evts" "$REMOVED" "$server4_token" "$client_addr_id"
+ verify_remove_event "$server_evts" "$REMOVED" "$server4_token" "$client_addr_id"
# RM_ADDR from the client to server machine
- :>"$evts"
+ :>"$server_evts"
client_addr_id=$(( client_addr_id - 1 ))
ip netns exec "$ns2" ./pm_nl_ctl rem token "$client4_token" id\
$client_addr_id > /dev/null 2>&1
stdbuf -o0 -e0 printf "RM_ADDR id:%d ns2 => ns1 \t"\
$client_addr_id
sleep 0.5
- verify_remove_event "$evts" "$REMOVED" "$server4_token" "$client_addr_id"
+ verify_remove_event "$server_evts" "$REMOVED" "$server4_token" "$client_addr_id"
# RM_ADDR6 from the client to server machine
- :>"$evts"
+ :>"$server_evts"
ip netns exec "$ns2" ./pm_nl_ctl rem token "$client6_token" id\
$client_addr_id > /dev/null 2>&1
stdbuf -o0 -e0 printf "RM_ADDR6 id:%d ns2 => ns1 \t"\
$client_addr_id
sleep 0.5
- verify_remove_event "$evts" "$REMOVED" "$server6_token" "$client_addr_id"
-
- kill_wait $evts_pid
+ verify_remove_event "$server_evts" "$REMOVED" "$server6_token" "$client_addr_id"
# Capture events on the network namespace running the client
- :>"$evts"
- ip netns exec "$ns2" ./pm_nl_ctl events >> "$evts" 2>&1 &
- evts_pid=$!
- sleep 0.5
+ :>"$client_evts"
# RM_ADDR from the server to client machine
ip netns exec "$ns1" ./pm_nl_ctl rem token "$server4_token" id\
@@ -436,27 +428,24 @@ test_remove()
stdbuf -o0 -e0 printf "RM_ADDR id:%d ns1 => ns2 \t"\
$server_addr_id
sleep 0.5
- verify_remove_event "$evts" "$REMOVED" "$client4_token" "$server_addr_id"
+ verify_remove_event "$client_evts" "$REMOVED" "$client4_token" "$server_addr_id"
# RM_ADDR from the server to client machine
- :>"$evts"
+ :>"$client_evts"
server_addr_id=$(( server_addr_id - 1 ))
ip netns exec "$ns1" ./pm_nl_ctl rem token "$server4_token" id\
$server_addr_id > /dev/null 2>&1
stdbuf -o0 -e0 printf "RM_ADDR id:%d ns1 => ns2 \t" $server_addr_id
sleep 0.5
- verify_remove_event "$evts" "$REMOVED" "$client4_token" "$server_addr_id"
+ verify_remove_event "$client_evts" "$REMOVED" "$client4_token" "$server_addr_id"
# RM_ADDR6 from the server to client machine
- :>"$evts"
+ :>"$client_evts"
ip netns exec "$ns1" ./pm_nl_ctl rem token "$server6_token" id\
$server_addr_id > /dev/null 2>&1
stdbuf -o0 -e0 printf "RM_ADDR6 id:%d ns1 => ns2 \t" $server_addr_id
sleep 0.5
- verify_remove_event "$evts" "$REMOVED" "$client6_token" "$server_addr_id"
-
- kill_wait $evts_pid
- rm -f "$evts"
+ verify_remove_event "$client_evts" "$REMOVED" "$client6_token" "$server_addr_id"
}
verify_subflow_events()
@@ -532,13 +521,8 @@ verify_subflow_events()
test_subflows()
{
- local evts
- evts=$(mktemp)
# Capture events on the network namespace running the server
- :>"$evts"
- ip netns exec "$ns1" ./pm_nl_ctl events >> "$evts" 2>&1 &
- evts_pid=$!
- sleep 0.5
+ :>"$server_evts"
# Attempt to add a listener at 10.0.2.2:<subflow-port>
ip netns exec "$ns2" ./pm_nl_ctl listen 10.0.2.2\
@@ -551,25 +535,25 @@ test_subflows()
sleep 0.5
# CREATE_SUBFLOW from server to client machine
- :>"$evts"
+ :>"$server_evts"
ip netns exec "$ns1" ./pm_nl_ctl csf lip 10.0.2.1 lid 23 rip 10.0.2.2\
rport "$client4_port" token "$server4_token" > /dev/null 2>&1
sleep 0.5
- verify_subflow_events "$evts" "$SUB_ESTABLISHED" "$server4_token" "$AF_INET" "10.0.2.1"\
+ verify_subflow_events $server_evts $SUB_ESTABLISHED $server4_token $AF_INET "10.0.2.1" \
"10.0.2.2" "$client4_port" "23" "$client_addr_id" "ns1" "ns2"
# Delete the listener from the client ns, if one was created
kill_wait $listener_pid
local sport
- sport=$(sed --unbuffered -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
+ sport=$(sed --unbuffered -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$server_evts")
# DESTROY_SUBFLOW from server to client machine
- :>"$evts"
+ :>"$server_evts"
ip netns exec "$ns1" ./pm_nl_ctl dsf lip 10.0.2.1 lport "$sport" rip 10.0.2.2 rport\
"$client4_port" token "$server4_token" > /dev/null 2>&1
sleep 0.5
- verify_subflow_events "$evts" "$SUB_CLOSED" "$server4_token" "$AF_INET" "10.0.2.1"\
+ verify_subflow_events "$server_evts" "$SUB_CLOSED" "$server4_token" "$AF_INET" "10.0.2.1"\
"10.0.2.2" "$client4_port" "23" "$client_addr_id" "ns1" "ns2"
# RM_ADDR from client to server machine
@@ -583,31 +567,31 @@ test_subflows()
listener_pid=$!
# ADD_ADDR6 from client to server machine reusing the subflow port
- :>"$evts"
+ :>"$server_evts"
ip netns exec "$ns2" ./pm_nl_ctl ann dead:beef:2::2 token "$client6_token" id\
$client_addr_id > /dev/null 2>&1
sleep 0.5
# CREATE_SUBFLOW6 from server to client machine
- :>"$evts"
+ :>"$server_evts"
ip netns exec "$ns1" ./pm_nl_ctl csf lip dead:beef:2::1 lid 23 rip\
dead:beef:2::2 rport "$client6_port" token "$server6_token" > /dev/null 2>&1
sleep 0.5
- verify_subflow_events "$evts" "$SUB_ESTABLISHED" "$server6_token" "$AF_INET6"\
+ verify_subflow_events "$server_evts" "$SUB_ESTABLISHED" "$server6_token" "$AF_INET6"\
"dead:beef:2::1" "dead:beef:2::2" "$client6_port" "23"\
"$client_addr_id" "ns1" "ns2"
# Delete the listener from the client ns, if one was created
kill_wait $listener_pid
- sport=$(sed --unbuffered -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
+ sport=$(sed --unbuffered -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$server_evts")
# DESTROY_SUBFLOW6 from server to client machine
- :>"$evts"
+ :>"$server_evts"
ip netns exec "$ns1" ./pm_nl_ctl dsf lip dead:beef:2::1 lport "$sport" rip\
dead:beef:2::2 rport "$client6_port" token "$server6_token" > /dev/null 2>&1
sleep 0.5
- verify_subflow_events "$evts" "$SUB_CLOSED" "$server6_token" "$AF_INET6"\
+ verify_subflow_events "$server_evts" "$SUB_CLOSED" "$server6_token" "$AF_INET6"\
"dead:beef:2::1" "dead:beef:2::2" "$client6_port" "23"\
"$client_addr_id" "ns1" "ns2"
@@ -622,44 +606,39 @@ test_subflows()
listener_pid=$!
# ADD_ADDR from client to server machine using a new port
- :>"$evts"
+ :>"$server_evts"
ip netns exec "$ns2" ./pm_nl_ctl ann 10.0.2.2 token "$client4_token" id\
$client_addr_id port $new4_port > /dev/null 2>&1
sleep 0.5
# CREATE_SUBFLOW from server to client machine
- :>"$evts"
+ :>"$server_evts"
ip netns exec "$ns1" ./pm_nl_ctl csf lip 10.0.2.1 lid 23 rip 10.0.2.2 rport\
$new4_port token "$server4_token" > /dev/null 2>&1
sleep 0.5
- verify_subflow_events "$evts" "$SUB_ESTABLISHED" "$server4_token" "$AF_INET"\
+ verify_subflow_events "$server_evts" "$SUB_ESTABLISHED" "$server4_token" "$AF_INET"\
"10.0.2.1" "10.0.2.2" "$new4_port" "23"\
"$client_addr_id" "ns1" "ns2"
# Delete the listener from the client ns, if one was created
kill_wait $listener_pid
- sport=$(sed --unbuffered -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
+ sport=$(sed --unbuffered -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$server_evts")
# DESTROY_SUBFLOW from server to client machine
- :>"$evts"
+ :>"$server_evts"
ip netns exec "$ns1" ./pm_nl_ctl dsf lip 10.0.2.1 lport "$sport" rip 10.0.2.2 rport\
$new4_port token "$server4_token" > /dev/null 2>&1
sleep 0.5
- verify_subflow_events "$evts" "$SUB_CLOSED" "$server4_token" "$AF_INET" "10.0.2.1"\
+ verify_subflow_events "$server_evts" "$SUB_CLOSED" "$server4_token" "$AF_INET" "10.0.2.1"\
"10.0.2.2" "$new4_port" "23" "$client_addr_id" "ns1" "ns2"
# RM_ADDR from client to server machine
ip netns exec "$ns2" ./pm_nl_ctl rem id $client_addr_id token\
"$client4_token" > /dev/null 2>&1
- kill_wait $evts_pid
-
# Capture events on the network namespace running the client
- :>"$evts"
- ip netns exec "$ns2" ./pm_nl_ctl events >> "$evts" 2>&1 &
- evts_pid=$!
- sleep 0.5
+ :>"$client_evts"
# Attempt to add a listener at 10.0.2.1:<subflow-port>
ip netns exec "$ns1" ./pm_nl_ctl listen 10.0.2.1\
@@ -672,24 +651,24 @@ test_subflows()
sleep 0.5
# CREATE_SUBFLOW from client to server machine
- :>"$evts"
+ :>"$client_evts"
ip netns exec "$ns2" ./pm_nl_ctl csf lip 10.0.2.2 lid 23 rip 10.0.2.1 rport\
$app4_port token "$client4_token" > /dev/null 2>&1
sleep 0.5
- verify_subflow_events "$evts" "$SUB_ESTABLISHED" "$client4_token" "$AF_INET" "10.0.2.2"\
+ verify_subflow_events $client_evts $SUB_ESTABLISHED $client4_token $AF_INET "10.0.2.2"\
"10.0.2.1" "$app4_port" "23" "$server_addr_id" "ns2" "ns1"
# Delete the listener from the server ns, if one was created
kill_wait $listener_pid
- sport=$(sed --unbuffered -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
+ sport=$(sed --unbuffered -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$client_evts")
# DESTROY_SUBFLOW from client to server machine
- :>"$evts"
+ :>"$client_evts"
ip netns exec "$ns2" ./pm_nl_ctl dsf lip 10.0.2.2 lport "$sport" rip 10.0.2.1 rport\
$app4_port token "$client4_token" > /dev/null 2>&1
sleep 0.5
- verify_subflow_events "$evts" "$SUB_CLOSED" "$client4_token" "$AF_INET" "10.0.2.2"\
+ verify_subflow_events "$client_evts" "$SUB_CLOSED" "$client4_token" "$AF_INET" "10.0.2.2"\
"10.0.2.1" "$app4_port" "23" "$server_addr_id" "ns2" "ns1"
# RM_ADDR from server to client machine
@@ -703,17 +682,17 @@ test_subflows()
listener_pid=$!
# ADD_ADDR6 from server to client machine reusing the subflow port
- :>"$evts"
+ :>"$client_evts"
ip netns exec "$ns1" ./pm_nl_ctl ann dead:beef:2::1 token "$server6_token" id\
$server_addr_id > /dev/null 2>&1
sleep 0.5
# CREATE_SUBFLOW6 from client to server machine
- :>"$evts"
+ :>"$client_evts"
ip netns exec "$ns2" ./pm_nl_ctl csf lip dead:beef:2::2 lid 23 rip\
dead:beef:2::1 rport $app6_port token "$client6_token" > /dev/null 2>&1
sleep 0.5
- verify_subflow_events "$evts" "$SUB_ESTABLISHED" "$client6_token"\
+ verify_subflow_events "$client_evts" "$SUB_ESTABLISHED" "$client6_token"\
"$AF_INET6" "dead:beef:2::2"\
"dead:beef:2::1" "$app6_port" "23"\
"$server_addr_id" "ns2" "ns1"
@@ -721,14 +700,14 @@ test_subflows()
# Delete the listener from the server ns, if one was created
kill_wait $listener_pid
- sport=$(sed --unbuffered -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
+ sport=$(sed --unbuffered -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$client_evts")
# DESTROY_SUBFLOW6 from client to server machine
- :>"$evts"
+ :>"$client_evts"
ip netns exec "$ns2" ./pm_nl_ctl dsf lip dead:beef:2::2 lport "$sport" rip\
dead:beef:2::1 rport $app6_port token "$client6_token" > /dev/null 2>&1
sleep 0.5
- verify_subflow_events "$evts" "$SUB_CLOSED" "$client6_token" "$AF_INET6" "dead:beef:2::2"\
+ verify_subflow_events $client_evts $SUB_CLOSED $client6_token $AF_INET6 "dead:beef:2::2"\
"dead:beef:2::1" "$app6_port" "23" "$server_addr_id" "ns2" "ns1"
# RM_ADDR6 from server to client machine
@@ -742,38 +721,35 @@ test_subflows()
listener_pid=$!
# ADD_ADDR from server to client machine using a new port
- :>"$evts"
+ :>"$client_evts"
ip netns exec "$ns1" ./pm_nl_ctl ann 10.0.2.1 token "$server4_token" id\
$server_addr_id port $new4_port > /dev/null 2>&1
sleep 0.5
# CREATE_SUBFLOW from client to server machine
- :>"$evts"
+ :>"$client_evts"
ip netns exec "$ns2" ./pm_nl_ctl csf lip 10.0.2.2 lid 23 rip 10.0.2.1 rport\
$new4_port token "$client4_token" > /dev/null 2>&1
sleep 0.5
- verify_subflow_events "$evts" "$SUB_ESTABLISHED" "$client4_token" "$AF_INET"\
+ verify_subflow_events "$client_evts" "$SUB_ESTABLISHED" "$client4_token" "$AF_INET"\
"10.0.2.2" "10.0.2.1" "$new4_port" "23" "$server_addr_id" "ns2" "ns1"
# Delete the listener from the server ns, if one was created
kill_wait $listener_pid
- sport=$(sed --unbuffered -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$evts")
+ sport=$(sed --unbuffered -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$client_evts")
# DESTROY_SUBFLOW from client to server machine
- :>"$evts"
+ :>"$client_evts"
ip netns exec "$ns2" ./pm_nl_ctl dsf lip 10.0.2.2 lport "$sport" rip 10.0.2.1 rport\
$new4_port token "$client4_token" > /dev/null 2>&1
sleep 0.5
- verify_subflow_events "$evts" "$SUB_CLOSED" "$client4_token" "$AF_INET" "10.0.2.2"\
+ verify_subflow_events "$client_evts" "$SUB_CLOSED" "$client4_token" "$AF_INET" "10.0.2.2"\
"10.0.2.1" "$new4_port" "23" "$server_addr_id" "ns2" "ns1"
# RM_ADDR from server to client machine
ip netns exec "$ns1" ./pm_nl_ctl rem id $server_addr_id token\
"$server4_token" > /dev/null 2>&1
-
- kill_wait $evts_pid
- rm -f "$evts"
}
test_prio()
@@ -807,11 +783,85 @@ test_prio()
fi
}
+verify_listener_events()
+{
+ local evt=$1
+ local e_type=$2
+ local e_family=$3
+ local e_saddr=$4
+ local e_sport=$5
+ local type
+ local family
+ local saddr
+ local sport
+
+ if [ $e_type = $LISTENER_CREATED ]; then
+ stdbuf -o0 -e0 printf "CREATE_LISTENER %s:%s\t\t\t\t\t"\
+ $e_saddr $e_sport
+ elif [ $e_type = $LISTENER_CLOSED ]; then
+ stdbuf -o0 -e0 printf "CLOSE_LISTENER %s:%s\t\t\t\t\t"\
+ $e_saddr $e_sport
+ fi
+
+ type=$(grep "type:$e_type," $evt |
+ sed --unbuffered -n 's/.*\(type:\)\([[:digit:]]*\).*$/\2/p;q')
+ family=$(grep "type:$e_type," $evt |
+ sed --unbuffered -n 's/.*\(family:\)\([[:digit:]]*\).*$/\2/p;q')
+ sport=$(grep "type:$e_type," $evt |
+ sed --unbuffered -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q')
+ if [ $family ] && [ $family = $AF_INET6 ]; then
+ saddr=$(grep "type:$e_type," $evt |
+ sed --unbuffered -n 's/.*\(saddr6:\)\([0-9a-f:.]*\).*$/\2/p;q')
+ else
+ saddr=$(grep "type:$e_type," $evt |
+ sed --unbuffered -n 's/.*\(saddr4:\)\([0-9.]*\).*$/\2/p;q')
+ fi
+
+ if [ $type ] && [ $type = $e_type ] &&
+ [ $family ] && [ $family = $e_family ] &&
+ [ $saddr ] && [ $saddr = $e_saddr ] &&
+ [ $sport ] && [ $sport = $e_sport ]; then
+ stdbuf -o0 -e0 printf "[OK]\n"
+ return 0
+ fi
+ stdbuf -o0 -e0 printf "[FAIL]\n"
+ exit 1
+}
+
+test_listener()
+{
+ # Capture events on the network namespace running the client
+ :>$client_evts
+
+ # Attempt to add a listener at 10.0.2.2:<subflow-port>
+ ip netns exec $ns2 ./pm_nl_ctl listen 10.0.2.2\
+ $client4_port > /dev/null 2>&1 &
+ local listener_pid=$!
+
+ verify_listener_events $client_evts $LISTENER_CREATED $AF_INET 10.0.2.2 $client4_port
+
+ # ADD_ADDR from client to server machine reusing the subflow port
+ ip netns exec $ns2 ./pm_nl_ctl ann 10.0.2.2 token $client4_token id\
+ $client_addr_id > /dev/null 2>&1
+ sleep 0.5
+
+ # CREATE_SUBFLOW from server to client machine
+ ip netns exec $ns1 ./pm_nl_ctl csf lip 10.0.2.1 lid 23 rip 10.0.2.2\
+ rport $client4_port token $server4_token > /dev/null 2>&1
+ sleep 0.5
+
+ # Delete the listener from the client ns, if one was created
+ kill_wait $listener_pid
+
+ verify_listener_events $client_evts $LISTENER_CLOSED $AF_INET 10.0.2.2 $client4_port
+}
+
make_connection
make_connection "v6"
test_announce
test_remove
test_subflows
test_prio
+test_listener
exit 0