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
iwl_mld_init(void)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
iwl_mld_exit(void)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
iwl_mld_hw_set_regulatory(struct iwl_mld * mld)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
iwl_construct_mld(struct iwl_mld * mld,struct iwl_trans * trans,const struct iwl_cfg * cfg,const struct iwl_fw * fw,struct ieee80211_hw * hw,struct dentry * dbgfs_dir)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)
iwl_mld_fwrt_dump_start(void * ctx)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)
iwl_mld_fwrt_dump_end(void * ctx)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
iwl_mld_d3_debug_enable(void * ctx)107d1e879ecSMiri Korenblit static bool iwl_mld_d3_debug_enable(void *ctx)
108d1e879ecSMiri Korenblit {
109d1e879ecSMiri Korenblit return IWL_MLD_D3_DEBUG;
110d1e879ecSMiri Korenblit }
111d1e879ecSMiri Korenblit
iwl_mld_fwrt_send_hcmd(void * ctx,struct iwl_host_cmd * host_cmd)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
iwl_mld_construct_fw_runtime(struct iwl_mld * mld,struct iwl_trans * trans,const struct iwl_fw * fw,struct dentry * debugfs_dir)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
iwl_mld_configure_trans(struct iwl_op_mode * op_mode)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 *
iwl_op_mode_mld_start(struct iwl_trans * trans,const struct iwl_cfg * cfg,const struct iwl_fw * fw,struct dentry * dbgfs_dir)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
iwl_op_mode_mld_stop(struct iwl_op_mode * op_mode)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
iwl_mld_queue_state_change(struct iwl_op_mode * op_mode,int hw_queue,bool queue_full)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
iwl_mld_queue_full(struct iwl_op_mode * op_mode,int hw_queue)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
iwl_mld_queue_not_full(struct iwl_op_mode * op_mode,int hw_queue)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
iwl_mld_set_hw_rfkill_state(struct iwl_op_mode * op_mode,bool state)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
iwl_mld_free_skb(struct iwl_op_mode * op_mode,struct sk_buff * skb)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
iwl_mld_read_error_recovery_buffer(struct iwl_mld * mld)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
iwl_mld_restart_nic(struct iwl_mld * mld)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
iwl_mld_nic_error(struct iwl_op_mode * op_mode,enum iwl_fw_error_type type)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
iwl_mld_dump_error(struct iwl_op_mode * op_mode,struct iwl_fw_error_dump_mode * mode)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
iwl_mld_sw_reset(struct iwl_op_mode * op_mode,enum iwl_fw_error_type type)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
iwl_mld_time_point(struct iwl_op_mode * op_mode,enum iwl_fw_ini_time_point tp_id,union iwl_dbg_tlv_tp_data * tp_data)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
iwl_mld_device_powered_off(struct iwl_op_mode * op_mode)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
iwl_mld_device_powered_off(struct iwl_op_mode * op_mode)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