1d1e879ecSMiri Korenblit // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2d1e879ecSMiri Korenblit /* 3d1e879ecSMiri Korenblit * Copyright (C) 2024-2025 Intel Corporation 4d1e879ecSMiri Korenblit */ 5*6895d74cSJohannes Berg #include <linux/rtnetlink.h> 6d1e879ecSMiri Korenblit #include <net/mac80211.h> 7d1e879ecSMiri Korenblit 8d1e879ecSMiri Korenblit #include "fw/api/rx.h" 9d1e879ecSMiri Korenblit #include "fw/api/datapath.h" 10d1e879ecSMiri Korenblit #include "fw/api/commands.h" 11d1e879ecSMiri Korenblit #include "fw/api/offload.h" 12d1e879ecSMiri Korenblit #include "fw/api/coex.h" 13d1e879ecSMiri Korenblit #include "fw/dbg.h" 14d1e879ecSMiri Korenblit #include "fw/uefi.h" 15d1e879ecSMiri Korenblit 16d1e879ecSMiri Korenblit #include "mld.h" 17d1e879ecSMiri Korenblit #include "mlo.h" 18d1e879ecSMiri Korenblit #include "mac80211.h" 19d1e879ecSMiri Korenblit #include "led.h" 20d1e879ecSMiri Korenblit #include "scan.h" 21d1e879ecSMiri Korenblit #include "tx.h" 22d1e879ecSMiri Korenblit #include "sta.h" 23d1e879ecSMiri Korenblit #include "regulatory.h" 24d1e879ecSMiri Korenblit #include "thermal.h" 25d1e879ecSMiri Korenblit #include "low_latency.h" 26d1e879ecSMiri Korenblit #include "hcmd.h" 27d1e879ecSMiri Korenblit #include "fw/api/location.h" 28d1e879ecSMiri Korenblit 29d1e879ecSMiri Korenblit #define DRV_DESCRIPTION "Intel(R) MLD wireless driver for Linux" 30d1e879ecSMiri Korenblit MODULE_DESCRIPTION(DRV_DESCRIPTION); 31d1e879ecSMiri Korenblit MODULE_LICENSE("GPL"); 32d1e879ecSMiri Korenblit MODULE_IMPORT_NS("IWLWIFI"); 33d1e879ecSMiri Korenblit 34d1e879ecSMiri Korenblit static const struct iwl_op_mode_ops iwl_mld_ops; 35d1e879ecSMiri Korenblit 36d1e879ecSMiri Korenblit static int __init iwl_mld_init(void) 37d1e879ecSMiri Korenblit { 38d1e879ecSMiri Korenblit int ret = iwl_opmode_register("iwlmld", &iwl_mld_ops); 39d1e879ecSMiri Korenblit 40d1e879ecSMiri Korenblit if (ret) 41d1e879ecSMiri Korenblit pr_err("Unable to register MLD op_mode: %d\n", ret); 42d1e879ecSMiri Korenblit 43d1e879ecSMiri Korenblit return ret; 44d1e879ecSMiri Korenblit } 45d1e879ecSMiri Korenblit module_init(iwl_mld_init); 46d1e879ecSMiri Korenblit 47d1e879ecSMiri Korenblit static void __exit iwl_mld_exit(void) 48d1e879ecSMiri Korenblit { 49d1e879ecSMiri Korenblit iwl_opmode_deregister("iwlmld"); 50d1e879ecSMiri Korenblit } 51d1e879ecSMiri Korenblit module_exit(iwl_mld_exit); 52d1e879ecSMiri Korenblit 53*6895d74cSJohannes Berg static void iwl_mld_hw_set_regulatory(struct iwl_mld *mld) 54*6895d74cSJohannes Berg { 55*6895d74cSJohannes Berg struct wiphy *wiphy = mld->wiphy; 56*6895d74cSJohannes Berg 57*6895d74cSJohannes Berg wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED; 58*6895d74cSJohannes Berg wiphy->regulatory_flags |= REGULATORY_ENABLE_RELAX_NO_IR; 59*6895d74cSJohannes Berg } 60*6895d74cSJohannes Berg 61d1e879ecSMiri Korenblit VISIBLE_IF_IWLWIFI_KUNIT 62d1e879ecSMiri Korenblit void iwl_construct_mld(struct iwl_mld *mld, struct iwl_trans *trans, 63d1e879ecSMiri Korenblit const struct iwl_cfg *cfg, const struct iwl_fw *fw, 64d1e879ecSMiri Korenblit struct ieee80211_hw *hw, struct dentry *dbgfs_dir) 65d1e879ecSMiri Korenblit { 66d1e879ecSMiri Korenblit mld->dev = trans->dev; 67d1e879ecSMiri Korenblit mld->trans = trans; 68d1e879ecSMiri Korenblit mld->cfg = cfg; 69d1e879ecSMiri Korenblit mld->fw = fw; 70d1e879ecSMiri Korenblit mld->hw = hw; 71d1e879ecSMiri Korenblit mld->wiphy = hw->wiphy; 72d1e879ecSMiri Korenblit mld->debugfs_dir = dbgfs_dir; 73d1e879ecSMiri Korenblit 74d1e879ecSMiri Korenblit iwl_notification_wait_init(&mld->notif_wait); 75d1e879ecSMiri Korenblit 76d1e879ecSMiri Korenblit /* Setup async RX handling */ 77d1e879ecSMiri Korenblit spin_lock_init(&mld->async_handlers_lock); 78d1e879ecSMiri Korenblit wiphy_work_init(&mld->async_handlers_wk, 79d1e879ecSMiri Korenblit iwl_mld_async_handlers_wk); 80d1e879ecSMiri Korenblit 81d1e879ecSMiri Korenblit /* Dynamic Queue Allocation */ 82d1e879ecSMiri Korenblit spin_lock_init(&mld->add_txqs_lock); 83d1e879ecSMiri Korenblit INIT_LIST_HEAD(&mld->txqs_to_add); 84d1e879ecSMiri Korenblit wiphy_work_init(&mld->add_txqs_wk, iwl_mld_add_txqs_wk); 85d1e879ecSMiri Korenblit 86d1e879ecSMiri Korenblit /* Setup RX queues sync wait queue */ 87d1e879ecSMiri Korenblit init_waitqueue_head(&mld->rxq_sync.waitq); 88d1e879ecSMiri Korenblit } 89d1e879ecSMiri Korenblit EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_construct_mld); 90d1e879ecSMiri Korenblit 91d1e879ecSMiri Korenblit static void __acquires(&mld->wiphy->mtx) 92d1e879ecSMiri Korenblit iwl_mld_fwrt_dump_start(void *ctx) 93d1e879ecSMiri Korenblit { 94d1e879ecSMiri Korenblit struct iwl_mld *mld = ctx; 95d1e879ecSMiri Korenblit 96d1e879ecSMiri Korenblit wiphy_lock(mld->wiphy); 97d1e879ecSMiri Korenblit } 98d1e879ecSMiri Korenblit 99d1e879ecSMiri Korenblit static void __releases(&mld->wiphy->mtx) 100d1e879ecSMiri Korenblit iwl_mld_fwrt_dump_end(void *ctx) 101d1e879ecSMiri Korenblit { 102d1e879ecSMiri Korenblit struct iwl_mld *mld = ctx; 103d1e879ecSMiri Korenblit 104d1e879ecSMiri Korenblit wiphy_unlock(mld->wiphy); 105d1e879ecSMiri Korenblit } 106d1e879ecSMiri Korenblit 107d1e879ecSMiri Korenblit static bool iwl_mld_d3_debug_enable(void *ctx) 108d1e879ecSMiri Korenblit { 109d1e879ecSMiri Korenblit return IWL_MLD_D3_DEBUG; 110d1e879ecSMiri Korenblit } 111d1e879ecSMiri Korenblit 112d1e879ecSMiri Korenblit static int iwl_mld_fwrt_send_hcmd(void *ctx, struct iwl_host_cmd *host_cmd) 113d1e879ecSMiri Korenblit { 114d1e879ecSMiri Korenblit struct iwl_mld *mld = (struct iwl_mld *)ctx; 115d1e879ecSMiri Korenblit int ret; 116d1e879ecSMiri Korenblit 117d1e879ecSMiri Korenblit wiphy_lock(mld->wiphy); 118d1e879ecSMiri Korenblit ret = iwl_mld_send_cmd(mld, host_cmd); 119d1e879ecSMiri Korenblit wiphy_unlock(mld->wiphy); 120d1e879ecSMiri Korenblit 121d1e879ecSMiri Korenblit return ret; 122d1e879ecSMiri Korenblit } 123d1e879ecSMiri Korenblit 124d1e879ecSMiri Korenblit static const struct iwl_fw_runtime_ops iwl_mld_fwrt_ops = { 125d1e879ecSMiri Korenblit .dump_start = iwl_mld_fwrt_dump_start, 126d1e879ecSMiri Korenblit .dump_end = iwl_mld_fwrt_dump_end, 127d1e879ecSMiri Korenblit .send_hcmd = iwl_mld_fwrt_send_hcmd, 128d1e879ecSMiri Korenblit .d3_debug_enable = iwl_mld_d3_debug_enable, 129d1e879ecSMiri Korenblit }; 130d1e879ecSMiri Korenblit 131d1e879ecSMiri Korenblit static void 132d1e879ecSMiri Korenblit iwl_mld_construct_fw_runtime(struct iwl_mld *mld, struct iwl_trans *trans, 133d1e879ecSMiri Korenblit const struct iwl_fw *fw, 134d1e879ecSMiri Korenblit struct dentry *debugfs_dir) 135d1e879ecSMiri Korenblit { 136d1e879ecSMiri Korenblit iwl_fw_runtime_init(&mld->fwrt, trans, fw, &iwl_mld_fwrt_ops, mld, 137d1e879ecSMiri Korenblit NULL, NULL, debugfs_dir); 138d1e879ecSMiri Korenblit 139d1e879ecSMiri Korenblit iwl_fw_set_current_image(&mld->fwrt, IWL_UCODE_REGULAR); 140d1e879ecSMiri Korenblit } 141d1e879ecSMiri Korenblit 142d1e879ecSMiri Korenblit /* Please keep this array *SORTED* by hex value. 143d1e879ecSMiri Korenblit * Access is done through binary search 144d1e879ecSMiri Korenblit */ 145d1e879ecSMiri Korenblit static const struct iwl_hcmd_names iwl_mld_legacy_names[] = { 146d1e879ecSMiri Korenblit HCMD_NAME(UCODE_ALIVE_NTFY), 147d1e879ecSMiri Korenblit HCMD_NAME(INIT_COMPLETE_NOTIF), 148d1e879ecSMiri Korenblit HCMD_NAME(PHY_CONTEXT_CMD), 149d1e879ecSMiri Korenblit HCMD_NAME(SCAN_CFG_CMD), 150d1e879ecSMiri Korenblit HCMD_NAME(SCAN_REQ_UMAC), 151d1e879ecSMiri Korenblit HCMD_NAME(SCAN_ABORT_UMAC), 152d1e879ecSMiri Korenblit HCMD_NAME(SCAN_COMPLETE_UMAC), 153d1e879ecSMiri Korenblit HCMD_NAME(TX_CMD), 154d1e879ecSMiri Korenblit HCMD_NAME(TXPATH_FLUSH), 155d1e879ecSMiri Korenblit HCMD_NAME(LEDS_CMD), 156d1e879ecSMiri Korenblit HCMD_NAME(WNM_80211V_TIMING_MEASUREMENT_NOTIFICATION), 157d1e879ecSMiri Korenblit HCMD_NAME(WNM_80211V_TIMING_MEASUREMENT_CONFIRM_NOTIFICATION), 158d1e879ecSMiri Korenblit HCMD_NAME(SCAN_OFFLOAD_UPDATE_PROFILES_CMD), 159d1e879ecSMiri Korenblit HCMD_NAME(POWER_TABLE_CMD), 160d1e879ecSMiri Korenblit HCMD_NAME(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION), 161d1e879ecSMiri Korenblit HCMD_NAME(BEACON_NOTIFICATION), 162d1e879ecSMiri Korenblit HCMD_NAME(BEACON_TEMPLATE_CMD), 163d1e879ecSMiri Korenblit HCMD_NAME(TX_ANT_CONFIGURATION_CMD), 164d1e879ecSMiri Korenblit HCMD_NAME(REDUCE_TX_POWER_CMD), 165d1e879ecSMiri Korenblit HCMD_NAME(MISSED_BEACONS_NOTIFICATION), 166d1e879ecSMiri Korenblit HCMD_NAME(MAC_PM_POWER_TABLE), 167d1e879ecSMiri Korenblit HCMD_NAME(MFUART_LOAD_NOTIFICATION), 168d1e879ecSMiri Korenblit HCMD_NAME(RSS_CONFIG_CMD), 169d1e879ecSMiri Korenblit HCMD_NAME(SCAN_ITERATION_COMPLETE_UMAC), 170d1e879ecSMiri Korenblit HCMD_NAME(REPLY_RX_MPDU_CMD), 171d1e879ecSMiri Korenblit HCMD_NAME(BA_NOTIF), 172d1e879ecSMiri Korenblit HCMD_NAME(MCC_UPDATE_CMD), 173d1e879ecSMiri Korenblit HCMD_NAME(MCC_CHUB_UPDATE_CMD), 174d1e879ecSMiri Korenblit HCMD_NAME(MCAST_FILTER_CMD), 175d1e879ecSMiri Korenblit HCMD_NAME(REPLY_BEACON_FILTERING_CMD), 176d1e879ecSMiri Korenblit HCMD_NAME(PROT_OFFLOAD_CONFIG_CMD), 177d1e879ecSMiri Korenblit HCMD_NAME(MATCH_FOUND_NOTIFICATION), 178d1e879ecSMiri Korenblit HCMD_NAME(WOWLAN_PATTERNS), 179d1e879ecSMiri Korenblit HCMD_NAME(WOWLAN_CONFIGURATION), 180d1e879ecSMiri Korenblit HCMD_NAME(WOWLAN_TSC_RSC_PARAM), 181d1e879ecSMiri Korenblit HCMD_NAME(WOWLAN_KEK_KCK_MATERIAL), 182d1e879ecSMiri Korenblit HCMD_NAME(DEBUG_HOST_COMMAND), 183d1e879ecSMiri Korenblit HCMD_NAME(LDBG_CONFIG_CMD), 184d1e879ecSMiri Korenblit }; 185d1e879ecSMiri Korenblit 186d1e879ecSMiri Korenblit /* Please keep this array *SORTED* by hex value. 187d1e879ecSMiri Korenblit * Access is done through binary search 188d1e879ecSMiri Korenblit */ 189d1e879ecSMiri Korenblit static const struct iwl_hcmd_names iwl_mld_system_names[] = { 190d1e879ecSMiri Korenblit HCMD_NAME(SHARED_MEM_CFG_CMD), 191d1e879ecSMiri Korenblit HCMD_NAME(SOC_CONFIGURATION_CMD), 192d1e879ecSMiri Korenblit HCMD_NAME(INIT_EXTENDED_CFG_CMD), 193d1e879ecSMiri Korenblit HCMD_NAME(FW_ERROR_RECOVERY_CMD), 194d1e879ecSMiri Korenblit HCMD_NAME(RFI_GET_FREQ_TABLE_CMD), 195d1e879ecSMiri Korenblit HCMD_NAME(SYSTEM_STATISTICS_CMD), 196d1e879ecSMiri Korenblit HCMD_NAME(SYSTEM_STATISTICS_END_NOTIF), 197d1e879ecSMiri Korenblit }; 198d1e879ecSMiri Korenblit 199d1e879ecSMiri Korenblit /* Please keep this array *SORTED* by hex value. 200d1e879ecSMiri Korenblit * Access is done through binary search 201d1e879ecSMiri Korenblit */ 202d1e879ecSMiri Korenblit static const struct iwl_hcmd_names iwl_mld_reg_and_nvm_names[] = { 203d1e879ecSMiri Korenblit HCMD_NAME(LARI_CONFIG_CHANGE), 204d1e879ecSMiri Korenblit HCMD_NAME(NVM_GET_INFO), 205d1e879ecSMiri Korenblit HCMD_NAME(TAS_CONFIG), 206d1e879ecSMiri Korenblit HCMD_NAME(SAR_OFFSET_MAPPING_TABLE_CMD), 207d1e879ecSMiri Korenblit HCMD_NAME(MCC_ALLOWED_AP_TYPE_CMD), 208d1e879ecSMiri Korenblit }; 209d1e879ecSMiri Korenblit 210d1e879ecSMiri Korenblit /* Please keep this array *SORTED* by hex value. 211d1e879ecSMiri Korenblit * Access is done through binary search 212d1e879ecSMiri Korenblit */ 213d1e879ecSMiri Korenblit static const struct iwl_hcmd_names iwl_mld_debug_names[] = { 214d1e879ecSMiri Korenblit HCMD_NAME(HOST_EVENT_CFG), 215d1e879ecSMiri Korenblit HCMD_NAME(DBGC_SUSPEND_RESUME), 216d1e879ecSMiri Korenblit }; 217d1e879ecSMiri Korenblit 218d1e879ecSMiri Korenblit /* Please keep this array *SORTED* by hex value. 219d1e879ecSMiri Korenblit * Access is done through binary search 220d1e879ecSMiri Korenblit */ 221d1e879ecSMiri Korenblit static const struct iwl_hcmd_names iwl_mld_mac_conf_names[] = { 222d1e879ecSMiri Korenblit HCMD_NAME(LOW_LATENCY_CMD), 223d1e879ecSMiri Korenblit HCMD_NAME(SESSION_PROTECTION_CMD), 224d1e879ecSMiri Korenblit HCMD_NAME(MAC_CONFIG_CMD), 225d1e879ecSMiri Korenblit HCMD_NAME(LINK_CONFIG_CMD), 226d1e879ecSMiri Korenblit HCMD_NAME(STA_CONFIG_CMD), 227d1e879ecSMiri Korenblit HCMD_NAME(AUX_STA_CMD), 228d1e879ecSMiri Korenblit HCMD_NAME(STA_REMOVE_CMD), 229d1e879ecSMiri Korenblit HCMD_NAME(ROC_CMD), 230d1e879ecSMiri Korenblit HCMD_NAME(MISSED_BEACONS_NOTIF), 231d1e879ecSMiri Korenblit HCMD_NAME(EMLSR_TRANS_FAIL_NOTIF), 232d1e879ecSMiri Korenblit HCMD_NAME(ROC_NOTIF), 233d1e879ecSMiri Korenblit HCMD_NAME(CHANNEL_SWITCH_ERROR_NOTIF), 234d1e879ecSMiri Korenblit HCMD_NAME(SESSION_PROTECTION_NOTIF), 235d1e879ecSMiri Korenblit HCMD_NAME(PROBE_RESPONSE_DATA_NOTIF), 236d1e879ecSMiri Korenblit HCMD_NAME(CHANNEL_SWITCH_START_NOTIF), 237d1e879ecSMiri Korenblit }; 238d1e879ecSMiri Korenblit 239d1e879ecSMiri Korenblit /* Please keep this array *SORTED* by hex value. 240d1e879ecSMiri Korenblit * Access is done through binary search 241d1e879ecSMiri Korenblit */ 242d1e879ecSMiri Korenblit static const struct iwl_hcmd_names iwl_mld_data_path_names[] = { 243d1e879ecSMiri Korenblit HCMD_NAME(TRIGGER_RX_QUEUES_NOTIF_CMD), 244d1e879ecSMiri Korenblit HCMD_NAME(WNM_PLATFORM_PTM_REQUEST_CMD), 245d1e879ecSMiri Korenblit HCMD_NAME(WNM_80211V_TIMING_MEASUREMENT_CONFIG_CMD), 246d1e879ecSMiri Korenblit HCMD_NAME(RFH_QUEUE_CONFIG_CMD), 247d1e879ecSMiri Korenblit HCMD_NAME(TLC_MNG_CONFIG_CMD), 248d1e879ecSMiri Korenblit HCMD_NAME(RX_BAID_ALLOCATION_CONFIG_CMD), 249d1e879ecSMiri Korenblit HCMD_NAME(SCD_QUEUE_CONFIG_CMD), 250d1e879ecSMiri Korenblit HCMD_NAME(OMI_SEND_STATUS_NOTIF), 251d1e879ecSMiri Korenblit HCMD_NAME(ESR_MODE_NOTIF), 252d1e879ecSMiri Korenblit HCMD_NAME(MONITOR_NOTIF), 253d1e879ecSMiri Korenblit HCMD_NAME(TLC_MNG_UPDATE_NOTIF), 254d1e879ecSMiri Korenblit HCMD_NAME(MU_GROUP_MGMT_NOTIF), 255d1e879ecSMiri Korenblit }; 256d1e879ecSMiri Korenblit 257d1e879ecSMiri Korenblit /* Please keep this array *SORTED* by hex value. 258d1e879ecSMiri Korenblit * Access is done through binary search 259d1e879ecSMiri Korenblit */ 260d1e879ecSMiri Korenblit static const struct iwl_hcmd_names iwl_mld_location_names[] = { 261d1e879ecSMiri Korenblit HCMD_NAME(TOF_RANGE_REQ_CMD), 262d1e879ecSMiri Korenblit HCMD_NAME(TOF_RANGE_RESPONSE_NOTIF), 263d1e879ecSMiri Korenblit }; 264d1e879ecSMiri Korenblit 265d1e879ecSMiri Korenblit /* Please keep this array *SORTED* by hex value. 266d1e879ecSMiri Korenblit * Access is done through binary search 267d1e879ecSMiri Korenblit */ 268d1e879ecSMiri Korenblit static const struct iwl_hcmd_names iwl_mld_phy_names[] = { 269d1e879ecSMiri Korenblit HCMD_NAME(CMD_DTS_MEASUREMENT_TRIGGER_WIDE), 270d1e879ecSMiri Korenblit HCMD_NAME(CTDP_CONFIG_CMD), 271d1e879ecSMiri Korenblit HCMD_NAME(TEMP_REPORTING_THRESHOLDS_CMD), 272d1e879ecSMiri Korenblit HCMD_NAME(PER_CHAIN_LIMIT_OFFSET_CMD), 273d1e879ecSMiri Korenblit HCMD_NAME(CT_KILL_NOTIFICATION), 274d1e879ecSMiri Korenblit HCMD_NAME(DTS_MEASUREMENT_NOTIF_WIDE), 275d1e879ecSMiri Korenblit }; 276d1e879ecSMiri Korenblit 277d1e879ecSMiri Korenblit /* Please keep this array *SORTED* by hex value. 278d1e879ecSMiri Korenblit * Access is done through binary search 279d1e879ecSMiri Korenblit */ 280d1e879ecSMiri Korenblit static const struct iwl_hcmd_names iwl_mld_statistics_names[] = { 281d1e879ecSMiri Korenblit HCMD_NAME(STATISTICS_OPER_NOTIF), 282d1e879ecSMiri Korenblit HCMD_NAME(STATISTICS_OPER_PART1_NOTIF), 283d1e879ecSMiri Korenblit }; 284d1e879ecSMiri Korenblit 285d1e879ecSMiri Korenblit /* Please keep this array *SORTED* by hex value. 286d1e879ecSMiri Korenblit * Access is done through binary search 287d1e879ecSMiri Korenblit */ 288d1e879ecSMiri Korenblit static const struct iwl_hcmd_names iwl_mld_prot_offload_names[] = { 289d1e879ecSMiri Korenblit HCMD_NAME(STORED_BEACON_NTF), 290d1e879ecSMiri Korenblit }; 291d1e879ecSMiri Korenblit 292d1e879ecSMiri Korenblit /* Please keep this array *SORTED* by hex value. 293d1e879ecSMiri Korenblit * Access is done through binary search 294d1e879ecSMiri Korenblit */ 295d1e879ecSMiri Korenblit static const struct iwl_hcmd_names iwl_mld_coex_names[] = { 296d1e879ecSMiri Korenblit HCMD_NAME(PROFILE_NOTIF), 297d1e879ecSMiri Korenblit }; 298d1e879ecSMiri Korenblit 299d1e879ecSMiri Korenblit VISIBLE_IF_IWLWIFI_KUNIT 300d1e879ecSMiri Korenblit const struct iwl_hcmd_arr iwl_mld_groups[] = { 301d1e879ecSMiri Korenblit [LEGACY_GROUP] = HCMD_ARR(iwl_mld_legacy_names), 302d1e879ecSMiri Korenblit [LONG_GROUP] = HCMD_ARR(iwl_mld_legacy_names), 303d1e879ecSMiri Korenblit [SYSTEM_GROUP] = HCMD_ARR(iwl_mld_system_names), 304d1e879ecSMiri Korenblit [MAC_CONF_GROUP] = HCMD_ARR(iwl_mld_mac_conf_names), 305d1e879ecSMiri Korenblit [DATA_PATH_GROUP] = HCMD_ARR(iwl_mld_data_path_names), 306d1e879ecSMiri Korenblit [LOCATION_GROUP] = HCMD_ARR(iwl_mld_location_names), 307d1e879ecSMiri Korenblit [REGULATORY_AND_NVM_GROUP] = HCMD_ARR(iwl_mld_reg_and_nvm_names), 308d1e879ecSMiri Korenblit [DEBUG_GROUP] = HCMD_ARR(iwl_mld_debug_names), 309d1e879ecSMiri Korenblit [PHY_OPS_GROUP] = HCMD_ARR(iwl_mld_phy_names), 310d1e879ecSMiri Korenblit [STATISTICS_GROUP] = HCMD_ARR(iwl_mld_statistics_names), 311d1e879ecSMiri Korenblit [PROT_OFFLOAD_GROUP] = HCMD_ARR(iwl_mld_prot_offload_names), 312d1e879ecSMiri Korenblit [BT_COEX_GROUP] = HCMD_ARR(iwl_mld_coex_names), 313d1e879ecSMiri Korenblit }; 314d1e879ecSMiri Korenblit EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_groups); 315d1e879ecSMiri Korenblit 316d1e879ecSMiri Korenblit #if IS_ENABLED(CONFIG_IWLWIFI_KUNIT_TESTS) 317d1e879ecSMiri Korenblit const unsigned int global_iwl_mld_goups_size = ARRAY_SIZE(iwl_mld_groups); 318d1e879ecSMiri Korenblit EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(global_iwl_mld_goups_size); 319d1e879ecSMiri Korenblit #endif 320d1e879ecSMiri Korenblit 321d1e879ecSMiri Korenblit static void 322d1e879ecSMiri Korenblit iwl_mld_configure_trans(struct iwl_op_mode *op_mode) 323d1e879ecSMiri Korenblit { 324d1e879ecSMiri Korenblit const struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode); 325d1e879ecSMiri Korenblit static const u8 no_reclaim_cmds[] = {TX_CMD}; 326d1e879ecSMiri Korenblit struct iwl_trans_config trans_cfg = { 327d1e879ecSMiri Korenblit .op_mode = op_mode, 328d1e879ecSMiri Korenblit /* Rx is not supported yet, but add it to avoid warnings */ 329d1e879ecSMiri Korenblit .rx_buf_size = iwl_amsdu_size_to_rxb_size(), 330d1e879ecSMiri Korenblit .command_groups = iwl_mld_groups, 331d1e879ecSMiri Korenblit .command_groups_size = ARRAY_SIZE(iwl_mld_groups), 332d1e879ecSMiri Korenblit .fw_reset_handshake = true, 333d1e879ecSMiri Korenblit .queue_alloc_cmd_ver = 334d1e879ecSMiri Korenblit iwl_fw_lookup_cmd_ver(mld->fw, 335d1e879ecSMiri Korenblit WIDE_ID(DATA_PATH_GROUP, 336d1e879ecSMiri Korenblit SCD_QUEUE_CONFIG_CMD), 337d1e879ecSMiri Korenblit 0), 338d1e879ecSMiri Korenblit .no_reclaim_cmds = no_reclaim_cmds, 339d1e879ecSMiri Korenblit .n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds), 340d1e879ecSMiri Korenblit .cb_data_offs = offsetof(struct ieee80211_tx_info, 341d1e879ecSMiri Korenblit driver_data[2]), 342d1e879ecSMiri Korenblit }; 343d1e879ecSMiri Korenblit struct iwl_trans *trans = mld->trans; 344d1e879ecSMiri Korenblit 345d1e879ecSMiri Korenblit trans->rx_mpdu_cmd = REPLY_RX_MPDU_CMD; 346d1e879ecSMiri Korenblit trans->iml = mld->fw->iml; 347d1e879ecSMiri Korenblit trans->iml_len = mld->fw->iml_len; 348d1e879ecSMiri Korenblit trans->wide_cmd_header = true; 349d1e879ecSMiri Korenblit 350d1e879ecSMiri Korenblit iwl_trans_configure(trans, &trans_cfg); 351d1e879ecSMiri Korenblit } 352d1e879ecSMiri Korenblit 353d1e879ecSMiri Korenblit /* 354d1e879ecSMiri Korenblit ***************************************************** 355d1e879ecSMiri Korenblit * op mode ops functions 356d1e879ecSMiri Korenblit ***************************************************** 357d1e879ecSMiri Korenblit */ 358d1e879ecSMiri Korenblit 359d1e879ecSMiri Korenblit #define NUM_FW_LOAD_RETRIES 3 360d1e879ecSMiri Korenblit static struct iwl_op_mode * 361d1e879ecSMiri Korenblit iwl_op_mode_mld_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, 362d1e879ecSMiri Korenblit const struct iwl_fw *fw, struct dentry *dbgfs_dir) 363d1e879ecSMiri Korenblit { 364d1e879ecSMiri Korenblit struct ieee80211_hw *hw; 365d1e879ecSMiri Korenblit struct iwl_op_mode *op_mode; 366d1e879ecSMiri Korenblit struct iwl_mld *mld; 367d1e879ecSMiri Korenblit u32 eckv_value; 368d1e879ecSMiri Korenblit int ret; 369d1e879ecSMiri Korenblit 370d1e879ecSMiri Korenblit /* Allocate and initialize a new hardware device */ 371d1e879ecSMiri Korenblit hw = ieee80211_alloc_hw(sizeof(struct iwl_op_mode) + 372d1e879ecSMiri Korenblit sizeof(struct iwl_mld), 373d1e879ecSMiri Korenblit &iwl_mld_hw_ops); 374d1e879ecSMiri Korenblit if (!hw) 375d1e879ecSMiri Korenblit return ERR_PTR(-ENOMEM); 376d1e879ecSMiri Korenblit 377d1e879ecSMiri Korenblit op_mode = hw->priv; 378d1e879ecSMiri Korenblit 379d1e879ecSMiri Korenblit op_mode->ops = &iwl_mld_ops; 380d1e879ecSMiri Korenblit 381d1e879ecSMiri Korenblit mld = IWL_OP_MODE_GET_MLD(op_mode); 382d1e879ecSMiri Korenblit 383d1e879ecSMiri Korenblit iwl_construct_mld(mld, trans, cfg, fw, hw, dbgfs_dir); 384d1e879ecSMiri Korenblit 385d1e879ecSMiri Korenblit iwl_mld_construct_fw_runtime(mld, trans, fw, dbgfs_dir); 386d1e879ecSMiri Korenblit 387d1e879ecSMiri Korenblit iwl_mld_get_bios_tables(mld); 388d1e879ecSMiri Korenblit iwl_uefi_get_sgom_table(trans, &mld->fwrt); 389d1e879ecSMiri Korenblit iwl_uefi_get_step_table(trans); 390d1e879ecSMiri Korenblit if (iwl_bios_get_eckv(&mld->fwrt, &eckv_value)) 391d1e879ecSMiri Korenblit IWL_DEBUG_RADIO(mld, "ECKV table doesn't exist in BIOS\n"); 392d1e879ecSMiri Korenblit else 393d1e879ecSMiri Korenblit trans->ext_32khz_clock_valid = !!eckv_value; 394d1e879ecSMiri Korenblit iwl_bios_setup_step(trans, &mld->fwrt); 395d1e879ecSMiri Korenblit mld->bios_enable_puncturing = iwl_uefi_get_puncturing(&mld->fwrt); 396d1e879ecSMiri Korenblit 397*6895d74cSJohannes Berg iwl_mld_hw_set_regulatory(mld); 398*6895d74cSJohannes Berg 399d1e879ecSMiri Korenblit /* Configure transport layer with the opmode specific params */ 400d1e879ecSMiri Korenblit iwl_mld_configure_trans(op_mode); 401d1e879ecSMiri Korenblit 402*6895d74cSJohannes Berg /* needed for regulatory init */ 403*6895d74cSJohannes Berg rtnl_lock(); 404d1e879ecSMiri Korenblit /* Needed for sending commands */ 405d1e879ecSMiri Korenblit wiphy_lock(mld->wiphy); 406d1e879ecSMiri Korenblit 407d1e879ecSMiri Korenblit for (int i = 0; i < NUM_FW_LOAD_RETRIES; i++) { 408d1e879ecSMiri Korenblit ret = iwl_mld_load_fw(mld); 409d1e879ecSMiri Korenblit if (!ret) 410d1e879ecSMiri Korenblit break; 411d1e879ecSMiri Korenblit } 412d1e879ecSMiri Korenblit 413d1e879ecSMiri Korenblit if (ret) { 414d1e879ecSMiri Korenblit wiphy_unlock(mld->wiphy); 415*6895d74cSJohannes Berg rtnl_unlock(); 416d1e879ecSMiri Korenblit iwl_fw_flush_dumps(&mld->fwrt); 417d1e879ecSMiri Korenblit goto free_hw; 418d1e879ecSMiri Korenblit } 419d1e879ecSMiri Korenblit 420d1e879ecSMiri Korenblit iwl_mld_stop_fw(mld); 421d1e879ecSMiri Korenblit 422d1e879ecSMiri Korenblit wiphy_unlock(mld->wiphy); 423*6895d74cSJohannes Berg rtnl_unlock(); 424d1e879ecSMiri Korenblit 425d1e879ecSMiri Korenblit ret = iwl_mld_leds_init(mld); 426d1e879ecSMiri Korenblit if (ret) 427d1e879ecSMiri Korenblit goto free_nvm; 428d1e879ecSMiri Korenblit 429d1e879ecSMiri Korenblit ret = iwl_mld_alloc_scan_cmd(mld); 430d1e879ecSMiri Korenblit if (ret) 431d1e879ecSMiri Korenblit goto leds_exit; 432d1e879ecSMiri Korenblit 433d1e879ecSMiri Korenblit ret = iwl_mld_low_latency_init(mld); 434d1e879ecSMiri Korenblit if (ret) 435d1e879ecSMiri Korenblit goto free_scan_cmd; 436d1e879ecSMiri Korenblit 437d1e879ecSMiri Korenblit ret = iwl_mld_register_hw(mld); 438d1e879ecSMiri Korenblit if (ret) 439d1e879ecSMiri Korenblit goto low_latency_free; 440d1e879ecSMiri Korenblit 441d1e879ecSMiri Korenblit iwl_mld_toggle_tx_ant(mld, &mld->mgmt_tx_ant); 442d1e879ecSMiri Korenblit 443d1e879ecSMiri Korenblit iwl_mld_add_debugfs_files(mld, dbgfs_dir); 444d1e879ecSMiri Korenblit iwl_mld_thermal_initialize(mld); 445d1e879ecSMiri Korenblit 446d1e879ecSMiri Korenblit iwl_mld_ptp_init(mld); 447d1e879ecSMiri Korenblit 448d1e879ecSMiri Korenblit return op_mode; 449d1e879ecSMiri Korenblit 450d1e879ecSMiri Korenblit low_latency_free: 451d1e879ecSMiri Korenblit iwl_mld_low_latency_free(mld); 452d1e879ecSMiri Korenblit free_scan_cmd: 453d1e879ecSMiri Korenblit kfree(mld->scan.cmd); 454d1e879ecSMiri Korenblit leds_exit: 455d1e879ecSMiri Korenblit iwl_mld_leds_exit(mld); 456d1e879ecSMiri Korenblit free_nvm: 457d1e879ecSMiri Korenblit kfree(mld->nvm_data); 458d1e879ecSMiri Korenblit free_hw: 459d1e879ecSMiri Korenblit ieee80211_free_hw(mld->hw); 460d1e879ecSMiri Korenblit return ERR_PTR(ret); 461d1e879ecSMiri Korenblit } 462d1e879ecSMiri Korenblit 463d1e879ecSMiri Korenblit static void 464d1e879ecSMiri Korenblit iwl_op_mode_mld_stop(struct iwl_op_mode *op_mode) 465d1e879ecSMiri Korenblit { 466d1e879ecSMiri Korenblit struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode); 467d1e879ecSMiri Korenblit 468d1e879ecSMiri Korenblit iwl_mld_ptp_remove(mld); 469d1e879ecSMiri Korenblit iwl_mld_leds_exit(mld); 470d1e879ecSMiri Korenblit 471d1e879ecSMiri Korenblit wiphy_lock(mld->wiphy); 472d1e879ecSMiri Korenblit iwl_mld_thermal_exit(mld); 473d1e879ecSMiri Korenblit iwl_mld_low_latency_stop(mld); 474d1e879ecSMiri Korenblit iwl_mld_deinit_time_sync(mld); 475d1e879ecSMiri Korenblit wiphy_unlock(mld->wiphy); 476d1e879ecSMiri Korenblit 477d1e879ecSMiri Korenblit ieee80211_unregister_hw(mld->hw); 478d1e879ecSMiri Korenblit 479d1e879ecSMiri Korenblit iwl_fw_runtime_free(&mld->fwrt); 480d1e879ecSMiri Korenblit iwl_mld_low_latency_free(mld); 481d1e879ecSMiri Korenblit 482d1e879ecSMiri Korenblit iwl_trans_op_mode_leave(mld->trans); 483d1e879ecSMiri Korenblit 484d1e879ecSMiri Korenblit kfree(mld->nvm_data); 485d1e879ecSMiri Korenblit kfree(mld->scan.cmd); 486d1e879ecSMiri Korenblit kfree(mld->error_recovery_buf); 487d1e879ecSMiri Korenblit kfree(mld->mcast_filter_cmd); 488d1e879ecSMiri Korenblit 489d1e879ecSMiri Korenblit ieee80211_free_hw(mld->hw); 490d1e879ecSMiri Korenblit } 491d1e879ecSMiri Korenblit 492d1e879ecSMiri Korenblit static void iwl_mld_queue_state_change(struct iwl_op_mode *op_mode, 493d1e879ecSMiri Korenblit int hw_queue, bool queue_full) 494d1e879ecSMiri Korenblit { 495d1e879ecSMiri Korenblit struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode); 496d1e879ecSMiri Korenblit struct ieee80211_txq *txq; 497d1e879ecSMiri Korenblit struct iwl_mld_sta *mld_sta; 498d1e879ecSMiri Korenblit struct iwl_mld_txq *mld_txq; 499d1e879ecSMiri Korenblit 500d1e879ecSMiri Korenblit rcu_read_lock(); 501d1e879ecSMiri Korenblit 502d1e879ecSMiri Korenblit txq = rcu_dereference(mld->fw_id_to_txq[hw_queue]); 503d1e879ecSMiri Korenblit if (!txq) { 504d1e879ecSMiri Korenblit rcu_read_unlock(); 505d1e879ecSMiri Korenblit 506d1e879ecSMiri Korenblit if (queue_full) { 507d1e879ecSMiri Korenblit /* An internal queue is not expected to become full */ 508d1e879ecSMiri Korenblit IWL_WARN(mld, 509d1e879ecSMiri Korenblit "Internal hw_queue %d is full! stopping all queues\n", 510d1e879ecSMiri Korenblit hw_queue); 511d1e879ecSMiri Korenblit /* Stop all queues, as an internal queue is not 512d1e879ecSMiri Korenblit * mapped to a mac80211 one 513d1e879ecSMiri Korenblit */ 514d1e879ecSMiri Korenblit ieee80211_stop_queues(mld->hw); 515d1e879ecSMiri Korenblit } else { 516d1e879ecSMiri Korenblit ieee80211_wake_queues(mld->hw); 517d1e879ecSMiri Korenblit } 518d1e879ecSMiri Korenblit 519d1e879ecSMiri Korenblit return; 520d1e879ecSMiri Korenblit } 521d1e879ecSMiri Korenblit 522d1e879ecSMiri Korenblit mld_txq = iwl_mld_txq_from_mac80211(txq); 523d1e879ecSMiri Korenblit mld_sta = txq->sta ? iwl_mld_sta_from_mac80211(txq->sta) : NULL; 524d1e879ecSMiri Korenblit 525d1e879ecSMiri Korenblit mld_txq->status.stop_full = queue_full; 526d1e879ecSMiri Korenblit 527d1e879ecSMiri Korenblit if (!queue_full && mld_sta && 528d1e879ecSMiri Korenblit mld_sta->sta_state != IEEE80211_STA_NOTEXIST) { 529d1e879ecSMiri Korenblit local_bh_disable(); 530d1e879ecSMiri Korenblit iwl_mld_tx_from_txq(mld, txq); 531d1e879ecSMiri Korenblit local_bh_enable(); 532d1e879ecSMiri Korenblit } 533d1e879ecSMiri Korenblit 534d1e879ecSMiri Korenblit rcu_read_unlock(); 535d1e879ecSMiri Korenblit } 536d1e879ecSMiri Korenblit 537d1e879ecSMiri Korenblit static void 538d1e879ecSMiri Korenblit iwl_mld_queue_full(struct iwl_op_mode *op_mode, int hw_queue) 539d1e879ecSMiri Korenblit { 540d1e879ecSMiri Korenblit iwl_mld_queue_state_change(op_mode, hw_queue, true); 541d1e879ecSMiri Korenblit } 542d1e879ecSMiri Korenblit 543d1e879ecSMiri Korenblit static void 544d1e879ecSMiri Korenblit iwl_mld_queue_not_full(struct iwl_op_mode *op_mode, int hw_queue) 545d1e879ecSMiri Korenblit { 546d1e879ecSMiri Korenblit iwl_mld_queue_state_change(op_mode, hw_queue, false); 547d1e879ecSMiri Korenblit } 548d1e879ecSMiri Korenblit 549d1e879ecSMiri Korenblit static bool 550d1e879ecSMiri Korenblit iwl_mld_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state) 551d1e879ecSMiri Korenblit { 552d1e879ecSMiri Korenblit struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode); 553d1e879ecSMiri Korenblit 554d1e879ecSMiri Korenblit iwl_mld_set_hwkill(mld, state); 555d1e879ecSMiri Korenblit 556d1e879ecSMiri Korenblit return false; 557d1e879ecSMiri Korenblit } 558d1e879ecSMiri Korenblit 559d1e879ecSMiri Korenblit static void 560d1e879ecSMiri Korenblit iwl_mld_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb) 561d1e879ecSMiri Korenblit { 562d1e879ecSMiri Korenblit struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode); 563d1e879ecSMiri Korenblit struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); 564d1e879ecSMiri Korenblit 565d1e879ecSMiri Korenblit iwl_trans_free_tx_cmd(mld->trans, info->driver_data[1]); 566d1e879ecSMiri Korenblit ieee80211_free_txskb(mld->hw, skb); 567d1e879ecSMiri Korenblit } 568d1e879ecSMiri Korenblit 569d1e879ecSMiri Korenblit static void iwl_mld_read_error_recovery_buffer(struct iwl_mld *mld) 570d1e879ecSMiri Korenblit { 571d1e879ecSMiri Korenblit u32 src_size = mld->fw->ucode_capa.error_log_size; 572d1e879ecSMiri Korenblit u32 src_addr = mld->fw->ucode_capa.error_log_addr; 573d1e879ecSMiri Korenblit u8 *recovery_buf; 574d1e879ecSMiri Korenblit int ret; 575d1e879ecSMiri Korenblit 576d1e879ecSMiri Korenblit /* no recovery buffer size defined in a TLV */ 577d1e879ecSMiri Korenblit if (!src_size) 578d1e879ecSMiri Korenblit return; 579d1e879ecSMiri Korenblit 580d1e879ecSMiri Korenblit recovery_buf = kzalloc(src_size, GFP_ATOMIC); 581d1e879ecSMiri Korenblit if (!recovery_buf) 582d1e879ecSMiri Korenblit return; 583d1e879ecSMiri Korenblit 584d1e879ecSMiri Korenblit ret = iwl_trans_read_mem_bytes(mld->trans, src_addr, 585d1e879ecSMiri Korenblit recovery_buf, src_size); 586d1e879ecSMiri Korenblit if (ret) { 587d1e879ecSMiri Korenblit IWL_ERR(mld, "Failed to read error recovery buffer (%d)\n", 588d1e879ecSMiri Korenblit ret); 589d1e879ecSMiri Korenblit kfree(recovery_buf); 590d1e879ecSMiri Korenblit return; 591d1e879ecSMiri Korenblit } 592d1e879ecSMiri Korenblit 593d1e879ecSMiri Korenblit mld->error_recovery_buf = recovery_buf; 594d1e879ecSMiri Korenblit } 595d1e879ecSMiri Korenblit 596d1e879ecSMiri Korenblit static void iwl_mld_restart_nic(struct iwl_mld *mld) 597d1e879ecSMiri Korenblit { 598d1e879ecSMiri Korenblit iwl_mld_read_error_recovery_buffer(mld); 599d1e879ecSMiri Korenblit 600d1e879ecSMiri Korenblit mld->fwrt.trans->dbg.restart_required = false; 601d1e879ecSMiri Korenblit 602d1e879ecSMiri Korenblit ieee80211_restart_hw(mld->hw); 603d1e879ecSMiri Korenblit } 604d1e879ecSMiri Korenblit 605d1e879ecSMiri Korenblit static void 606d1e879ecSMiri Korenblit iwl_mld_nic_error(struct iwl_op_mode *op_mode, 607d1e879ecSMiri Korenblit enum iwl_fw_error_type type) 608d1e879ecSMiri Korenblit { 609d1e879ecSMiri Korenblit struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode); 610d1e879ecSMiri Korenblit bool trans_dead = test_bit(STATUS_TRANS_DEAD, &mld->trans->status); 611d1e879ecSMiri Korenblit 612d1e879ecSMiri Korenblit if (type == IWL_ERR_TYPE_CMD_QUEUE_FULL) 613d1e879ecSMiri Korenblit IWL_ERR(mld, "Command queue full!\n"); 614d1e879ecSMiri Korenblit else if (!trans_dead && !mld->fw_status.do_not_dump_once) 615d1e879ecSMiri Korenblit iwl_fwrt_dump_error_logs(&mld->fwrt); 616d1e879ecSMiri Korenblit 617d1e879ecSMiri Korenblit mld->fw_status.do_not_dump_once = false; 618d1e879ecSMiri Korenblit 619d1e879ecSMiri Korenblit /* It is necessary to abort any os scan here because mac80211 requires 620d1e879ecSMiri Korenblit * having the scan cleared before restarting. 621d1e879ecSMiri Korenblit * We'll reset the scan_status to NONE in restart cleanup in 622d1e879ecSMiri Korenblit * the next drv_start() call from mac80211. If ieee80211_hw_restart 623d1e879ecSMiri Korenblit * isn't called scan status will stay busy. 624d1e879ecSMiri Korenblit */ 625d1e879ecSMiri Korenblit iwl_mld_report_scan_aborted(mld); 626d1e879ecSMiri Korenblit 627d1e879ecSMiri Korenblit /* 628d1e879ecSMiri Korenblit * This should be first thing before trying to collect any 629d1e879ecSMiri Korenblit * data to avoid endless loops if any HW error happens while 630d1e879ecSMiri Korenblit * collecting debug data. 631d1e879ecSMiri Korenblit * It might not actually be true that we'll restart, but the 632d1e879ecSMiri Korenblit * setting doesn't matter if we're going to be unbound either. 633d1e879ecSMiri Korenblit */ 634d1e879ecSMiri Korenblit if (type != IWL_ERR_TYPE_RESET_HS_TIMEOUT) 635d1e879ecSMiri Korenblit mld->fw_status.in_hw_restart = true; 636d1e879ecSMiri Korenblit } 637d1e879ecSMiri Korenblit 638d1e879ecSMiri Korenblit static void iwl_mld_dump_error(struct iwl_op_mode *op_mode, 639d1e879ecSMiri Korenblit struct iwl_fw_error_dump_mode *mode) 640d1e879ecSMiri Korenblit { 641d1e879ecSMiri Korenblit struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode); 642d1e879ecSMiri Korenblit 643d1e879ecSMiri Korenblit /* if we come in from opmode we have the mutex held */ 644d1e879ecSMiri Korenblit if (mode->context == IWL_ERR_CONTEXT_FROM_OPMODE) { 645d1e879ecSMiri Korenblit lockdep_assert_wiphy(mld->wiphy); 646d1e879ecSMiri Korenblit iwl_fw_error_collect(&mld->fwrt); 647d1e879ecSMiri Korenblit } else { 648d1e879ecSMiri Korenblit wiphy_lock(mld->wiphy); 649d1e879ecSMiri Korenblit if (mode->context != IWL_ERR_CONTEXT_ABORT) 650d1e879ecSMiri Korenblit iwl_fw_error_collect(&mld->fwrt); 651d1e879ecSMiri Korenblit wiphy_unlock(mld->wiphy); 652d1e879ecSMiri Korenblit } 653d1e879ecSMiri Korenblit } 654d1e879ecSMiri Korenblit 655d1e879ecSMiri Korenblit static bool iwl_mld_sw_reset(struct iwl_op_mode *op_mode, 656d1e879ecSMiri Korenblit enum iwl_fw_error_type type) 657d1e879ecSMiri Korenblit { 658d1e879ecSMiri Korenblit struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode); 659d1e879ecSMiri Korenblit 660d1e879ecSMiri Korenblit /* Do restart only in the following conditions are met: 661d1e879ecSMiri Korenblit * - we consider the FW as running 662d1e879ecSMiri Korenblit * - The trigger that brought us here is defined as one that requires 663d1e879ecSMiri Korenblit * a restart (in the debug TLVs) 664d1e879ecSMiri Korenblit */ 665d1e879ecSMiri Korenblit if (!mld->fw_status.running || !mld->fwrt.trans->dbg.restart_required) 666d1e879ecSMiri Korenblit return false; 667d1e879ecSMiri Korenblit 668d1e879ecSMiri Korenblit iwl_mld_restart_nic(mld); 669d1e879ecSMiri Korenblit return true; 670d1e879ecSMiri Korenblit } 671d1e879ecSMiri Korenblit 672d1e879ecSMiri Korenblit static void 673d1e879ecSMiri Korenblit iwl_mld_time_point(struct iwl_op_mode *op_mode, 674d1e879ecSMiri Korenblit enum iwl_fw_ini_time_point tp_id, 675d1e879ecSMiri Korenblit union iwl_dbg_tlv_tp_data *tp_data) 676d1e879ecSMiri Korenblit { 677d1e879ecSMiri Korenblit struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode); 678d1e879ecSMiri Korenblit 679d1e879ecSMiri Korenblit iwl_dbg_tlv_time_point(&mld->fwrt, tp_id, tp_data); 680d1e879ecSMiri Korenblit } 681d1e879ecSMiri Korenblit 682d1e879ecSMiri Korenblit #ifdef CONFIG_PM_SLEEP 683d1e879ecSMiri Korenblit static void iwl_mld_device_powered_off(struct iwl_op_mode *op_mode) 684d1e879ecSMiri Korenblit { 685d1e879ecSMiri Korenblit struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode); 686d1e879ecSMiri Korenblit 687d1e879ecSMiri Korenblit wiphy_lock(mld->wiphy); 688d1e879ecSMiri Korenblit mld->trans->system_pm_mode = IWL_PLAT_PM_MODE_DISABLED; 689d1e879ecSMiri Korenblit iwl_mld_stop_fw(mld); 690d1e879ecSMiri Korenblit mld->fw_status.in_d3 = false; 691d1e879ecSMiri Korenblit wiphy_unlock(mld->wiphy); 692d1e879ecSMiri Korenblit } 693d1e879ecSMiri Korenblit #else 694d1e879ecSMiri Korenblit static void iwl_mld_device_powered_off(struct iwl_op_mode *op_mode) 695d1e879ecSMiri Korenblit {} 696d1e879ecSMiri Korenblit #endif 697d1e879ecSMiri Korenblit 698d1e879ecSMiri Korenblit static const struct iwl_op_mode_ops iwl_mld_ops = { 699d1e879ecSMiri Korenblit .start = iwl_op_mode_mld_start, 700d1e879ecSMiri Korenblit .stop = iwl_op_mode_mld_stop, 701d1e879ecSMiri Korenblit .rx = iwl_mld_rx, 702d1e879ecSMiri Korenblit .rx_rss = iwl_mld_rx_rss, 703d1e879ecSMiri Korenblit .queue_full = iwl_mld_queue_full, 704d1e879ecSMiri Korenblit .queue_not_full = iwl_mld_queue_not_full, 705d1e879ecSMiri Korenblit .hw_rf_kill = iwl_mld_set_hw_rfkill_state, 706d1e879ecSMiri Korenblit .free_skb = iwl_mld_free_skb, 707d1e879ecSMiri Korenblit .nic_error = iwl_mld_nic_error, 708d1e879ecSMiri Korenblit .dump_error = iwl_mld_dump_error, 709d1e879ecSMiri Korenblit .sw_reset = iwl_mld_sw_reset, 710d1e879ecSMiri Korenblit .time_point = iwl_mld_time_point, 711d1e879ecSMiri Korenblit .device_powered_off = pm_sleep_ptr(iwl_mld_device_powered_off), 712d1e879ecSMiri Korenblit }; 713d1e879ecSMiri Korenblit 714d1e879ecSMiri Korenblit struct iwl_mld_mod_params iwlmld_mod_params = { 715d1e879ecSMiri Korenblit .power_scheme = IWL_POWER_SCHEME_BPS, 716d1e879ecSMiri Korenblit }; 717d1e879ecSMiri Korenblit 718d1e879ecSMiri Korenblit module_param_named(power_scheme, iwlmld_mod_params.power_scheme, int, 0444); 719d1e879ecSMiri Korenblit MODULE_PARM_DESC(power_scheme, 720d1e879ecSMiri Korenblit "power management scheme: 1-active, 2-balanced, default: 2"); 721