xref: /linux/drivers/net/wireless/intel/iwlwifi/mld/fw.c (revision 2c7e4a2663a1ab5a740c59c31991579b6b865a26)
1d1e879ecSMiri Korenblit // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2d1e879ecSMiri Korenblit /*
3d1e879ecSMiri Korenblit  * Copyright (C) 2024-2025 Intel Corporation
4d1e879ecSMiri Korenblit  */
5d1e879ecSMiri Korenblit 
6d1e879ecSMiri Korenblit #include "mld.h"
7d1e879ecSMiri Korenblit 
8d1e879ecSMiri Korenblit #include "fw/api/alive.h"
9d1e879ecSMiri Korenblit #include "fw/api/scan.h"
10d1e879ecSMiri Korenblit #include "fw/api/rx.h"
111f263e63SMiri Korenblit #include "phy.h"
12d1e879ecSMiri Korenblit #include "fw/dbg.h"
13d1e879ecSMiri Korenblit #include "fw/pnvm.h"
14d1e879ecSMiri Korenblit #include "hcmd.h"
15d1e879ecSMiri Korenblit #include "power.h"
16d1e879ecSMiri Korenblit #include "mcc.h"
17d1e879ecSMiri Korenblit #include "led.h"
18d1e879ecSMiri Korenblit #include "coex.h"
19d1e879ecSMiri Korenblit #include "regulatory.h"
20d1e879ecSMiri Korenblit #include "thermal.h"
21d1e879ecSMiri Korenblit 
iwl_mld_send_tx_ant_cfg(struct iwl_mld * mld)22d1e879ecSMiri Korenblit static int iwl_mld_send_tx_ant_cfg(struct iwl_mld *mld)
23d1e879ecSMiri Korenblit {
24d1e879ecSMiri Korenblit 	struct iwl_tx_ant_cfg_cmd cmd;
25d1e879ecSMiri Korenblit 
26d1e879ecSMiri Korenblit 	lockdep_assert_wiphy(mld->wiphy);
27d1e879ecSMiri Korenblit 
28d1e879ecSMiri Korenblit 	cmd.valid = cpu_to_le32(iwl_mld_get_valid_tx_ant(mld));
29d1e879ecSMiri Korenblit 
30d1e879ecSMiri Korenblit 	IWL_DEBUG_FW(mld, "select valid tx ant: %u\n", cmd.valid);
31d1e879ecSMiri Korenblit 
32d1e879ecSMiri Korenblit 	return iwl_mld_send_cmd_pdu(mld, TX_ANT_CONFIGURATION_CMD, &cmd);
33d1e879ecSMiri Korenblit }
34d1e879ecSMiri Korenblit 
iwl_mld_send_rss_cfg_cmd(struct iwl_mld * mld)35d1e879ecSMiri Korenblit static int iwl_mld_send_rss_cfg_cmd(struct iwl_mld *mld)
36d1e879ecSMiri Korenblit {
37d1e879ecSMiri Korenblit 	struct iwl_rss_config_cmd cmd = {
38d1e879ecSMiri Korenblit 		.flags = cpu_to_le32(IWL_RSS_ENABLE),
39d1e879ecSMiri Korenblit 		.hash_mask = BIT(IWL_RSS_HASH_TYPE_IPV4_TCP) |
40d1e879ecSMiri Korenblit 			     BIT(IWL_RSS_HASH_TYPE_IPV4_UDP) |
41d1e879ecSMiri Korenblit 			     BIT(IWL_RSS_HASH_TYPE_IPV4_PAYLOAD) |
42d1e879ecSMiri Korenblit 			     BIT(IWL_RSS_HASH_TYPE_IPV6_TCP) |
43d1e879ecSMiri Korenblit 			     BIT(IWL_RSS_HASH_TYPE_IPV6_UDP) |
44d1e879ecSMiri Korenblit 			     BIT(IWL_RSS_HASH_TYPE_IPV6_PAYLOAD),
45d1e879ecSMiri Korenblit 	};
46d1e879ecSMiri Korenblit 
47d1e879ecSMiri Korenblit 	lockdep_assert_wiphy(mld->wiphy);
48d1e879ecSMiri Korenblit 
49d1e879ecSMiri Korenblit 	/* Do not direct RSS traffic to Q 0 which is our fallback queue */
50d1e879ecSMiri Korenblit 	for (int i = 0; i < ARRAY_SIZE(cmd.indirection_table); i++)
51d1e879ecSMiri Korenblit 		cmd.indirection_table[i] =
52d5861378SJohannes Berg 			1 + (i % (mld->trans->info.num_rxqs - 1));
53d1e879ecSMiri Korenblit 	netdev_rss_key_fill(cmd.secret_key, sizeof(cmd.secret_key));
54d1e879ecSMiri Korenblit 
55d1e879ecSMiri Korenblit 	return iwl_mld_send_cmd_pdu(mld, RSS_CONFIG_CMD, &cmd);
56d1e879ecSMiri Korenblit }
57d1e879ecSMiri Korenblit 
iwl_mld_config_scan(struct iwl_mld * mld)58d1e879ecSMiri Korenblit static int iwl_mld_config_scan(struct iwl_mld *mld)
59d1e879ecSMiri Korenblit {
60d1e879ecSMiri Korenblit 	struct iwl_scan_config cmd = {
61d1e879ecSMiri Korenblit 		.tx_chains = cpu_to_le32(iwl_mld_get_valid_tx_ant(mld)),
62d1e879ecSMiri Korenblit 		.rx_chains = cpu_to_le32(iwl_mld_get_valid_rx_ant(mld))
63d1e879ecSMiri Korenblit 	};
64d1e879ecSMiri Korenblit 
65d1e879ecSMiri Korenblit 	return iwl_mld_send_cmd_pdu(mld, WIDE_ID(LONG_GROUP, SCAN_CFG_CMD),
66d1e879ecSMiri Korenblit 				    &cmd);
67d1e879ecSMiri Korenblit }
68d1e879ecSMiri Korenblit 
iwl_mld_alive_imr_data(struct iwl_trans * trans,const struct iwl_imr_alive_info * imr_info)69d1e879ecSMiri Korenblit static void iwl_mld_alive_imr_data(struct iwl_trans *trans,
70d1e879ecSMiri Korenblit 				   const struct iwl_imr_alive_info *imr_info)
71d1e879ecSMiri Korenblit {
72d1e879ecSMiri Korenblit 	struct iwl_imr_data *imr_data = &trans->dbg.imr_data;
73d1e879ecSMiri Korenblit 
74d1e879ecSMiri Korenblit 	imr_data->imr_enable = le32_to_cpu(imr_info->enabled);
75d1e879ecSMiri Korenblit 	imr_data->imr_size = le32_to_cpu(imr_info->size);
76d1e879ecSMiri Korenblit 	imr_data->imr2sram_remainbyte = imr_data->imr_size;
77d1e879ecSMiri Korenblit 	imr_data->imr_base_addr = imr_info->base_addr;
78d1e879ecSMiri Korenblit 	imr_data->imr_curr_addr = le64_to_cpu(imr_data->imr_base_addr);
79d1e879ecSMiri Korenblit 
80d1e879ecSMiri Korenblit 	if (imr_data->imr_enable)
81d1e879ecSMiri Korenblit 		return;
82d1e879ecSMiri Korenblit 
83d1e879ecSMiri Korenblit 	for (int i = 0; i < ARRAY_SIZE(trans->dbg.active_regions); i++) {
84d1e879ecSMiri Korenblit 		struct iwl_fw_ini_region_tlv *reg;
85d1e879ecSMiri Korenblit 
86d1e879ecSMiri Korenblit 		if (!trans->dbg.active_regions[i])
87d1e879ecSMiri Korenblit 			continue;
88d1e879ecSMiri Korenblit 
89d1e879ecSMiri Korenblit 		reg = (void *)trans->dbg.active_regions[i]->data;
90d1e879ecSMiri Korenblit 
91d1e879ecSMiri Korenblit 		/* We have only one DRAM IMR region, so we
92d1e879ecSMiri Korenblit 		 * can break as soon as we find the first
93d1e879ecSMiri Korenblit 		 * one.
94d1e879ecSMiri Korenblit 		 */
95d1e879ecSMiri Korenblit 		if (reg->type == IWL_FW_INI_REGION_DRAM_IMR) {
96d1e879ecSMiri Korenblit 			trans->dbg.unsupported_region_msk |= BIT(i);
97d1e879ecSMiri Korenblit 			break;
98d1e879ecSMiri Korenblit 		}
99d1e879ecSMiri Korenblit 	}
100d1e879ecSMiri Korenblit }
101d1e879ecSMiri Korenblit 
1023a68ae0fSJohannes Berg struct iwl_mld_alive_data {
1033a68ae0fSJohannes Berg 	__le32 sku_id[3];
1043a68ae0fSJohannes Berg 	bool valid;
1053a68ae0fSJohannes Berg };
1063a68ae0fSJohannes Berg 
iwl_alive_fn(struct iwl_notif_wait_data * notif_wait,struct iwl_rx_packet * pkt,void * data)107d1e879ecSMiri Korenblit static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait,
108d1e879ecSMiri Korenblit 			 struct iwl_rx_packet *pkt, void *data)
109d1e879ecSMiri Korenblit {
110d1e879ecSMiri Korenblit 	unsigned int pkt_len = iwl_rx_packet_payload_len(pkt);
1110bd6ede7SEmmanuel Grumbach 	unsigned int expected_sz;
112d1e879ecSMiri Korenblit 	struct iwl_mld *mld =
113d1e879ecSMiri Korenblit 		container_of(notif_wait, struct iwl_mld, notif_wait);
114d1e879ecSMiri Korenblit 	struct iwl_trans *trans = mld->trans;
115d1e879ecSMiri Korenblit 	u32 version = iwl_fw_lookup_notif_ver(mld->fw, LEGACY_GROUP,
116d1e879ecSMiri Korenblit 					      UCODE_ALIVE_NTFY, 0);
1173a68ae0fSJohannes Berg 	struct iwl_mld_alive_data *alive_data = data;
1180bd6ede7SEmmanuel Grumbach 	struct iwl_alive_ntf *palive;
119d1e879ecSMiri Korenblit 	struct iwl_umac_alive *umac;
120d1e879ecSMiri Korenblit 	struct iwl_lmac_alive *lmac1;
121d1e879ecSMiri Korenblit 	struct iwl_lmac_alive *lmac2 = NULL;
122d1e879ecSMiri Korenblit 	u32 lmac_error_event_table;
123d1e879ecSMiri Korenblit 	u32 umac_error_table;
124d1e879ecSMiri Korenblit 	u16 status;
125d1e879ecSMiri Korenblit 
1260bd6ede7SEmmanuel Grumbach 	switch (version) {
1270bd6ede7SEmmanuel Grumbach 	case 6:
1280bd6ede7SEmmanuel Grumbach 	case 7:
1290bd6ede7SEmmanuel Grumbach 		expected_sz = sizeof(struct iwl_alive_ntf_v6);
1300bd6ede7SEmmanuel Grumbach 		break;
1310bd6ede7SEmmanuel Grumbach 	case 8:
1320bd6ede7SEmmanuel Grumbach 		expected_sz = sizeof(struct iwl_alive_ntf);
1330bd6ede7SEmmanuel Grumbach 		break;
1340bd6ede7SEmmanuel Grumbach 	default:
1350bd6ede7SEmmanuel Grumbach 		return false;
1360bd6ede7SEmmanuel Grumbach 	}
1370bd6ede7SEmmanuel Grumbach 
1380bd6ede7SEmmanuel Grumbach 	if (pkt_len != expected_sz)
139d1e879ecSMiri Korenblit 		return false;
140d1e879ecSMiri Korenblit 
141d1e879ecSMiri Korenblit 	palive = (void *)pkt->data;
142d1e879ecSMiri Korenblit 
143d1e879ecSMiri Korenblit 	iwl_mld_alive_imr_data(trans, &palive->imr);
144d1e879ecSMiri Korenblit 
145d1e879ecSMiri Korenblit 	umac = &palive->umac_data;
146d1e879ecSMiri Korenblit 	lmac1 = &palive->lmac_data[0];
147d1e879ecSMiri Korenblit 	lmac2 = &palive->lmac_data[1];
148d1e879ecSMiri Korenblit 	status = le16_to_cpu(palive->status);
149d1e879ecSMiri Korenblit 
1503a68ae0fSJohannes Berg 	BUILD_BUG_ON(sizeof(alive_data->sku_id) !=
1513a68ae0fSJohannes Berg 		     sizeof(palive->sku_id.data));
1523a68ae0fSJohannes Berg 	memcpy(alive_data->sku_id, palive->sku_id.data,
1533a68ae0fSJohannes Berg 	       sizeof(palive->sku_id.data));
154d1e879ecSMiri Korenblit 
155d1e879ecSMiri Korenblit 	IWL_DEBUG_FW(mld, "Got sku_id: 0x0%x 0x0%x 0x0%x\n",
1563a68ae0fSJohannes Berg 		     le32_to_cpu(alive_data->sku_id[0]),
1573a68ae0fSJohannes Berg 		     le32_to_cpu(alive_data->sku_id[1]),
1583a68ae0fSJohannes Berg 		     le32_to_cpu(alive_data->sku_id[2]));
159d1e879ecSMiri Korenblit 
160d1e879ecSMiri Korenblit 	lmac_error_event_table =
161d1e879ecSMiri Korenblit 		le32_to_cpu(lmac1->dbg_ptrs.error_event_table_ptr);
162d1e879ecSMiri Korenblit 	iwl_fw_lmac1_set_alive_err_table(trans, lmac_error_event_table);
163d1e879ecSMiri Korenblit 
164d1e879ecSMiri Korenblit 	if (lmac2)
165d1e879ecSMiri Korenblit 		trans->dbg.lmac_error_event_table[1] =
166d1e879ecSMiri Korenblit 			le32_to_cpu(lmac2->dbg_ptrs.error_event_table_ptr);
167d1e879ecSMiri Korenblit 
168d1e879ecSMiri Korenblit 	umac_error_table = le32_to_cpu(umac->dbg_ptrs.error_info_addr) &
169d1e879ecSMiri Korenblit 		~FW_ADDR_CACHE_CONTROL;
170d1e879ecSMiri Korenblit 
171857ecb85SJohannes Berg 	if (umac_error_table >= trans->mac_cfg->base->min_umac_error_event_table)
172d1e879ecSMiri Korenblit 		iwl_fw_umac_set_alive_err_table(trans, umac_error_table);
173d1e879ecSMiri Korenblit 	else
174d1e879ecSMiri Korenblit 		IWL_ERR(mld, "Not valid error log pointer 0x%08X\n",
175d1e879ecSMiri Korenblit 			umac_error_table);
176d1e879ecSMiri Korenblit 
1773a68ae0fSJohannes Berg 	alive_data->valid = status == IWL_ALIVE_STATUS_OK;
178d1e879ecSMiri Korenblit 
179d1e879ecSMiri Korenblit 	IWL_DEBUG_FW(mld,
180d1e879ecSMiri Korenblit 		     "Alive ucode status 0x%04x revision 0x%01X 0x%01X\n",
181d1e879ecSMiri Korenblit 		     status, lmac1->ver_type, lmac1->ver_subtype);
182d1e879ecSMiri Korenblit 
183d1e879ecSMiri Korenblit 	if (lmac2)
184d1e879ecSMiri Korenblit 		IWL_DEBUG_FW(mld, "Alive ucode CDB\n");
185d1e879ecSMiri Korenblit 
186d1e879ecSMiri Korenblit 	IWL_DEBUG_FW(mld,
187d1e879ecSMiri Korenblit 		     "UMAC version: Major - 0x%x, Minor - 0x%x\n",
188d1e879ecSMiri Korenblit 		     le32_to_cpu(umac->umac_major),
189d1e879ecSMiri Korenblit 		     le32_to_cpu(umac->umac_minor));
190d1e879ecSMiri Korenblit 
191d1e879ecSMiri Korenblit 	if (version >= 7)
192d1e879ecSMiri Korenblit 		IWL_DEBUG_FW(mld, "FW alive flags 0x%x\n",
193d1e879ecSMiri Korenblit 			     le16_to_cpu(palive->flags));
194d1e879ecSMiri Korenblit 
1950bd6ede7SEmmanuel Grumbach 	if (version >= 8)
1960bd6ede7SEmmanuel Grumbach 		IWL_DEBUG_FW(mld, "platform_id 0x%llx\n",
1970bd6ede7SEmmanuel Grumbach 			     le64_to_cpu(palive->platform_id));
1980bd6ede7SEmmanuel Grumbach 
199d1e879ecSMiri Korenblit 	iwl_fwrt_update_fw_versions(&mld->fwrt, lmac1, umac);
200d1e879ecSMiri Korenblit 
201d1e879ecSMiri Korenblit 	return true;
202d1e879ecSMiri Korenblit }
203d1e879ecSMiri Korenblit 
204d1e879ecSMiri Korenblit #define MLD_ALIVE_TIMEOUT		(2 * HZ)
205d1e879ecSMiri Korenblit #define MLD_INIT_COMPLETE_TIMEOUT	(2 * HZ)
206d1e879ecSMiri Korenblit 
iwl_mld_print_alive_notif_timeout(struct iwl_mld * mld)207d1e879ecSMiri Korenblit static void iwl_mld_print_alive_notif_timeout(struct iwl_mld *mld)
208d1e879ecSMiri Korenblit {
209d1e879ecSMiri Korenblit 	struct iwl_trans *trans = mld->trans;
210d1e879ecSMiri Korenblit 	struct iwl_pc_data *pc_data;
211d1e879ecSMiri Korenblit 	u8 count;
212d1e879ecSMiri Korenblit 
213d1e879ecSMiri Korenblit 	IWL_ERR(mld,
214d1e879ecSMiri Korenblit 		"SecBoot CPU1 Status: 0x%x, CPU2 Status: 0x%x\n",
215d1e879ecSMiri Korenblit 		iwl_read_umac_prph(trans, UMAG_SB_CPU_1_STATUS),
216d1e879ecSMiri Korenblit 		iwl_read_umac_prph(trans,
217d1e879ecSMiri Korenblit 				   UMAG_SB_CPU_2_STATUS));
218d1e879ecSMiri Korenblit #define IWL_FW_PRINT_REG_INFO(reg_name) \
219d1e879ecSMiri Korenblit 	IWL_ERR(mld, #reg_name ": 0x%x\n", iwl_read_umac_prph(trans, reg_name))
220d1e879ecSMiri Korenblit 
221d1e879ecSMiri Korenblit 	IWL_FW_PRINT_REG_INFO(WFPM_LMAC1_PD_NOTIFICATION);
222d1e879ecSMiri Korenblit 
223d1e879ecSMiri Korenblit 	IWL_FW_PRINT_REG_INFO(HPM_SECONDARY_DEVICE_STATE);
224d1e879ecSMiri Korenblit 
225d1e879ecSMiri Korenblit 	/* print OTP info */
226d1e879ecSMiri Korenblit 	IWL_FW_PRINT_REG_INFO(WFPM_MAC_OTP_CFG7_ADDR);
227d1e879ecSMiri Korenblit 	IWL_FW_PRINT_REG_INFO(WFPM_MAC_OTP_CFG7_DATA);
228d1e879ecSMiri Korenblit #undef IWL_FW_PRINT_REG_INFO
229d1e879ecSMiri Korenblit 
230d1e879ecSMiri Korenblit 	pc_data = trans->dbg.pc_data;
231d1e879ecSMiri Korenblit 	for (count = 0; count < trans->dbg.num_pc; count++, pc_data++)
232d1e879ecSMiri Korenblit 		IWL_ERR(mld, "%s: 0x%x\n", pc_data->pc_name,
233d1e879ecSMiri Korenblit 			pc_data->pc_address);
234d1e879ecSMiri Korenblit }
235d1e879ecSMiri Korenblit 
iwl_mld_load_fw_wait_alive(struct iwl_mld * mld,struct iwl_mld_alive_data * alive_data)2363a68ae0fSJohannes Berg static int iwl_mld_load_fw_wait_alive(struct iwl_mld *mld,
2373a68ae0fSJohannes Berg 				      struct iwl_mld_alive_data *alive_data)
238d1e879ecSMiri Korenblit {
239d1e879ecSMiri Korenblit 	static const u16 alive_cmd[] = { UCODE_ALIVE_NTFY };
240d1e879ecSMiri Korenblit 	struct iwl_notification_wait alive_wait;
241d1e879ecSMiri Korenblit 	int ret;
242d1e879ecSMiri Korenblit 
243d1e879ecSMiri Korenblit 	lockdep_assert_wiphy(mld->wiphy);
244d1e879ecSMiri Korenblit 
245d1e879ecSMiri Korenblit 	iwl_init_notification_wait(&mld->notif_wait, &alive_wait,
246d1e879ecSMiri Korenblit 				   alive_cmd, ARRAY_SIZE(alive_cmd),
2473a68ae0fSJohannes Berg 				   iwl_alive_fn, alive_data);
248d1e879ecSMiri Korenblit 
249d1e879ecSMiri Korenblit 	iwl_dbg_tlv_time_point(&mld->fwrt, IWL_FW_INI_TIME_POINT_EARLY, NULL);
250d1e879ecSMiri Korenblit 
251a94d0189SJohannes Berg 	ret = iwl_trans_start_fw(mld->trans, mld->fw, IWL_UCODE_REGULAR, true);
252d1e879ecSMiri Korenblit 	if (ret) {
253d1e879ecSMiri Korenblit 		iwl_remove_notification(&mld->notif_wait, &alive_wait);
254d1e879ecSMiri Korenblit 		return ret;
255d1e879ecSMiri Korenblit 	}
256d1e879ecSMiri Korenblit 
257d1e879ecSMiri Korenblit 	ret = iwl_wait_notification(&mld->notif_wait, &alive_wait,
258d1e879ecSMiri Korenblit 				    MLD_ALIVE_TIMEOUT);
259d1e879ecSMiri Korenblit 
260d1e879ecSMiri Korenblit 	if (ret) {
261d1e879ecSMiri Korenblit 		if (ret == -ETIMEDOUT)
262d1e879ecSMiri Korenblit 			iwl_fw_dbg_error_collect(&mld->fwrt,
263d1e879ecSMiri Korenblit 						 FW_DBG_TRIGGER_ALIVE_TIMEOUT);
264d1e879ecSMiri Korenblit 		iwl_mld_print_alive_notif_timeout(mld);
265fab65a1aSMiri Korenblit 		return ret;
266d1e879ecSMiri Korenblit 	}
267d1e879ecSMiri Korenblit 
2683a68ae0fSJohannes Berg 	if (!alive_data->valid) {
269d1e879ecSMiri Korenblit 		IWL_ERR(mld, "Loaded firmware is not valid!\n");
270fab65a1aSMiri Korenblit 		return -EIO;
271d1e879ecSMiri Korenblit 	}
272d1e879ecSMiri Korenblit 
273d43c01d3SJohannes Berg 	iwl_trans_fw_alive(mld->trans);
274d1e879ecSMiri Korenblit 
275d1e879ecSMiri Korenblit 	return 0;
276d1e879ecSMiri Korenblit }
277d1e879ecSMiri Korenblit 
iwl_mld_run_fw_init_sequence(struct iwl_mld * mld)278892998c7SMiri Korenblit static int iwl_mld_run_fw_init_sequence(struct iwl_mld *mld)
279d1e879ecSMiri Korenblit {
280d1e879ecSMiri Korenblit 	struct iwl_notification_wait init_wait;
2811f263e63SMiri Korenblit 	struct iwl_init_extended_cfg_cmd init_cfg = {
2821f263e63SMiri Korenblit 		.init_flags = cpu_to_le32(BIT(IWL_INIT_PHY)),
2831f263e63SMiri Korenblit 	};
2843a68ae0fSJohannes Berg 	struct iwl_mld_alive_data alive_data = {};
285d1e879ecSMiri Korenblit 	static const u16 init_complete[] = {
286d1e879ecSMiri Korenblit 		INIT_COMPLETE_NOTIF,
287d1e879ecSMiri Korenblit 	};
288d1e879ecSMiri Korenblit 	int ret;
289d1e879ecSMiri Korenblit 
290d1e879ecSMiri Korenblit 	lockdep_assert_wiphy(mld->wiphy);
291d1e879ecSMiri Korenblit 
2923a68ae0fSJohannes Berg 	ret = iwl_mld_load_fw_wait_alive(mld, &alive_data);
293d1e879ecSMiri Korenblit 	if (ret)
294d1e879ecSMiri Korenblit 		return ret;
295d1e879ecSMiri Korenblit 
296d1e879ecSMiri Korenblit 	ret = iwl_pnvm_load(mld->trans, &mld->notif_wait,
2973a68ae0fSJohannes Berg 			    &mld->fw->ucode_capa, alive_data.sku_id);
298d1e879ecSMiri Korenblit 	if (ret) {
299d1e879ecSMiri Korenblit 		IWL_ERR(mld, "Timeout waiting for PNVM load %d\n", ret);
300fab65a1aSMiri Korenblit 		return ret;
301d1e879ecSMiri Korenblit 	}
302d1e879ecSMiri Korenblit 
303d1e879ecSMiri Korenblit 	iwl_dbg_tlv_time_point(&mld->fwrt, IWL_FW_INI_TIME_POINT_AFTER_ALIVE,
304d1e879ecSMiri Korenblit 			       NULL);
305d1e879ecSMiri Korenblit 
306d1e879ecSMiri Korenblit 	iwl_init_notification_wait(&mld->notif_wait,
307d1e879ecSMiri Korenblit 				   &init_wait,
308d1e879ecSMiri Korenblit 				   init_complete,
309d1e879ecSMiri Korenblit 				   ARRAY_SIZE(init_complete),
310d1e879ecSMiri Korenblit 				   NULL, NULL);
311d1e879ecSMiri Korenblit 
312d1e879ecSMiri Korenblit 	ret = iwl_mld_send_cmd_pdu(mld,
313d1e879ecSMiri Korenblit 				   WIDE_ID(SYSTEM_GROUP, INIT_EXTENDED_CFG_CMD),
314d1e879ecSMiri Korenblit 				   &init_cfg);
315d1e879ecSMiri Korenblit 	if (ret) {
316d1e879ecSMiri Korenblit 		IWL_ERR(mld, "Failed to send init config command: %d\n", ret);
317d1e879ecSMiri Korenblit 		iwl_remove_notification(&mld->notif_wait, &init_wait);
318fab65a1aSMiri Korenblit 		return ret;
319d1e879ecSMiri Korenblit 	}
320d1e879ecSMiri Korenblit 
3211f263e63SMiri Korenblit 	ret = iwl_mld_send_phy_cfg_cmd(mld);
3221f263e63SMiri Korenblit 	if (ret) {
3231f263e63SMiri Korenblit 		IWL_ERR(mld, "Failed to send PHY config command: %d\n", ret);
3241f263e63SMiri Korenblit 		iwl_remove_notification(&mld->notif_wait, &init_wait);
3251f263e63SMiri Korenblit 		return ret;
3261f263e63SMiri Korenblit 	}
3271f263e63SMiri Korenblit 
328d1e879ecSMiri Korenblit 	ret = iwl_wait_notification(&mld->notif_wait, &init_wait,
329d1e879ecSMiri Korenblit 				    MLD_INIT_COMPLETE_TIMEOUT);
330d1e879ecSMiri Korenblit 	if (ret) {
331d1e879ecSMiri Korenblit 		IWL_ERR(mld, "Failed to get INIT_COMPLETE %d\n", ret);
332fab65a1aSMiri Korenblit 		return ret;
333d1e879ecSMiri Korenblit 	}
334d1e879ecSMiri Korenblit 
335d1e879ecSMiri Korenblit 	return 0;
336d1e879ecSMiri Korenblit }
337d1e879ecSMiri Korenblit 
iwl_mld_load_fw(struct iwl_mld * mld)338d1e879ecSMiri Korenblit int iwl_mld_load_fw(struct iwl_mld *mld)
339d1e879ecSMiri Korenblit {
340d1e879ecSMiri Korenblit 	int ret;
341d1e879ecSMiri Korenblit 
342d1e879ecSMiri Korenblit 	lockdep_assert_wiphy(mld->wiphy);
343d1e879ecSMiri Korenblit 
344d1e879ecSMiri Korenblit 	ret = iwl_trans_start_hw(mld->trans);
345d1e879ecSMiri Korenblit 	if (ret)
346d1e879ecSMiri Korenblit 		return ret;
347d1e879ecSMiri Korenblit 
348d1e879ecSMiri Korenblit 	ret = iwl_mld_run_fw_init_sequence(mld);
349d1e879ecSMiri Korenblit 	if (ret)
350fab65a1aSMiri Korenblit 		goto err;
351d1e879ecSMiri Korenblit 
352d1e879ecSMiri Korenblit 	mld->fw_status.running = true;
353d1e879ecSMiri Korenblit 
354d1e879ecSMiri Korenblit 	return 0;
355fab65a1aSMiri Korenblit err:
356e1d35eabSMiri Korenblit 	iwl_mld_stop_fw(mld);
357fab65a1aSMiri Korenblit 	return ret;
358d1e879ecSMiri Korenblit }
359d1e879ecSMiri Korenblit 
iwl_mld_stop_fw(struct iwl_mld * mld)360d1e879ecSMiri Korenblit void iwl_mld_stop_fw(struct iwl_mld *mld)
361d1e879ecSMiri Korenblit {
362d1e879ecSMiri Korenblit 	lockdep_assert_wiphy(mld->wiphy);
363d1e879ecSMiri Korenblit 
364d1e879ecSMiri Korenblit 	iwl_abort_notification_waits(&mld->notif_wait);
365d1e879ecSMiri Korenblit 
366d1e879ecSMiri Korenblit 	iwl_fw_dbg_stop_sync(&mld->fwrt);
367d1e879ecSMiri Korenblit 
368d1e879ecSMiri Korenblit 	iwl_trans_stop_device(mld->trans);
369d1e879ecSMiri Korenblit 
370b68df31cSMiri Korenblit 	/* HW is stopped, no more coming RX. Cancel all notifications in
371b68df31cSMiri Korenblit 	 * case they were sent just before stopping the HW.
372b68df31cSMiri Korenblit 	 */
373b68df31cSMiri Korenblit 	iwl_mld_cancel_async_notifications(mld);
374b68df31cSMiri Korenblit 
375d1e879ecSMiri Korenblit 	mld->fw_status.running = false;
376d1e879ecSMiri Korenblit }
377d1e879ecSMiri Korenblit 
iwl_mld_restart_disconnect_iter(void * data,u8 * mac,struct ieee80211_vif * vif)378d1e879ecSMiri Korenblit static void iwl_mld_restart_disconnect_iter(void *data, u8 *mac,
379d1e879ecSMiri Korenblit 					    struct ieee80211_vif *vif)
380d1e879ecSMiri Korenblit {
381d1e879ecSMiri Korenblit 	if (vif->type == NL80211_IFTYPE_STATION)
382d1e879ecSMiri Korenblit 		ieee80211_hw_restart_disconnect(vif);
383d1e879ecSMiri Korenblit }
384d1e879ecSMiri Korenblit 
iwl_mld_send_recovery_cmd(struct iwl_mld * mld,u32 flags)385d1e879ecSMiri Korenblit void iwl_mld_send_recovery_cmd(struct iwl_mld *mld, u32 flags)
386d1e879ecSMiri Korenblit {
387d1e879ecSMiri Korenblit 	u32 error_log_size = mld->fw->ucode_capa.error_log_size;
388d1e879ecSMiri Korenblit 	struct iwl_fw_error_recovery_cmd recovery_cmd = {
389d1e879ecSMiri Korenblit 		.flags = cpu_to_le32(flags),
390d1e879ecSMiri Korenblit 	};
391d1e879ecSMiri Korenblit 	struct iwl_host_cmd cmd = {
392d1e879ecSMiri Korenblit 		.id = WIDE_ID(SYSTEM_GROUP, FW_ERROR_RECOVERY_CMD),
393d1e879ecSMiri Korenblit 		.flags = CMD_WANT_SKB,
394d1e879ecSMiri Korenblit 		.data = {&recovery_cmd, },
395d1e879ecSMiri Korenblit 		.len = {sizeof(recovery_cmd), },
396d1e879ecSMiri Korenblit 	};
397d1e879ecSMiri Korenblit 	int ret;
398d1e879ecSMiri Korenblit 
399d1e879ecSMiri Korenblit 	/* no error log was defined in TLV */
400d1e879ecSMiri Korenblit 	if (!error_log_size)
401d1e879ecSMiri Korenblit 		return;
402d1e879ecSMiri Korenblit 
403d1e879ecSMiri Korenblit 	if (flags & ERROR_RECOVERY_UPDATE_DB) {
404d1e879ecSMiri Korenblit 		/* no buf was allocated upon NIC error */
405d1e879ecSMiri Korenblit 		if (!mld->error_recovery_buf)
406d1e879ecSMiri Korenblit 			return;
407d1e879ecSMiri Korenblit 
408d1e879ecSMiri Korenblit 		cmd.data[1] = mld->error_recovery_buf;
409d1e879ecSMiri Korenblit 		cmd.len[1] =  error_log_size;
410d1e879ecSMiri Korenblit 		cmd.dataflags[1] = IWL_HCMD_DFL_NOCOPY;
411d1e879ecSMiri Korenblit 		recovery_cmd.buf_size = cpu_to_le32(error_log_size);
412d1e879ecSMiri Korenblit 	}
413d1e879ecSMiri Korenblit 
414d1e879ecSMiri Korenblit 	ret = iwl_mld_send_cmd(mld, &cmd);
415d1e879ecSMiri Korenblit 
416d1e879ecSMiri Korenblit 	/* we no longer need the recovery buffer */
417d1e879ecSMiri Korenblit 	kfree(mld->error_recovery_buf);
418d1e879ecSMiri Korenblit 	mld->error_recovery_buf = NULL;
419d1e879ecSMiri Korenblit 
420d1e879ecSMiri Korenblit 	if (ret) {
421d1e879ecSMiri Korenblit 		IWL_ERR(mld, "Failed to send recovery cmd %d\n", ret);
422d1e879ecSMiri Korenblit 		return;
423d1e879ecSMiri Korenblit 	}
424d1e879ecSMiri Korenblit 
425d1e879ecSMiri Korenblit 	if (flags & ERROR_RECOVERY_UPDATE_DB) {
426d1e879ecSMiri Korenblit 		struct iwl_rx_packet *pkt = cmd.resp_pkt;
427d1e879ecSMiri Korenblit 		u32 pkt_len = iwl_rx_packet_payload_len(pkt);
428d1e879ecSMiri Korenblit 		u32 resp;
429d1e879ecSMiri Korenblit 
430d1e879ecSMiri Korenblit 		if (IWL_FW_CHECK(mld, pkt_len != sizeof(resp),
431d1e879ecSMiri Korenblit 				 "Unexpected recovery cmd response size %u (expected %zu)\n",
432d1e879ecSMiri Korenblit 				 pkt_len, sizeof(resp)))
433d1e879ecSMiri Korenblit 			goto out;
434d1e879ecSMiri Korenblit 
435d1e879ecSMiri Korenblit 		resp = le32_to_cpup((__le32 *)cmd.resp_pkt->data);
436d1e879ecSMiri Korenblit 		if (!resp)
437d1e879ecSMiri Korenblit 			goto out;
438d1e879ecSMiri Korenblit 
439d1e879ecSMiri Korenblit 		IWL_ERR(mld,
440d1e879ecSMiri Korenblit 			"Failed to send recovery cmd blob was invalid %d\n",
441d1e879ecSMiri Korenblit 			resp);
442d1e879ecSMiri Korenblit 
443d1e879ecSMiri Korenblit 		ieee80211_iterate_interfaces(mld->hw, 0,
444d1e879ecSMiri Korenblit 					     iwl_mld_restart_disconnect_iter,
445d1e879ecSMiri Korenblit 					     NULL);
446d1e879ecSMiri Korenblit 	}
447d1e879ecSMiri Korenblit 
448d1e879ecSMiri Korenblit out:
449d1e879ecSMiri Korenblit 	iwl_free_resp(&cmd);
450d1e879ecSMiri Korenblit }
451d1e879ecSMiri Korenblit 
iwl_mld_config_fw(struct iwl_mld * mld)452d1e879ecSMiri Korenblit static int iwl_mld_config_fw(struct iwl_mld *mld)
453d1e879ecSMiri Korenblit {
454d1e879ecSMiri Korenblit 	int ret;
455d1e879ecSMiri Korenblit 
456d1e879ecSMiri Korenblit 	lockdep_assert_wiphy(mld->wiphy);
457d1e879ecSMiri Korenblit 
458d1e879ecSMiri Korenblit 	iwl_fw_disable_dbg_asserts(&mld->fwrt);
459d1e879ecSMiri Korenblit 	iwl_get_shared_mem_conf(&mld->fwrt);
460d1e879ecSMiri Korenblit 
461d1e879ecSMiri Korenblit 	ret = iwl_mld_send_tx_ant_cfg(mld);
462d1e879ecSMiri Korenblit 	if (ret)
463d1e879ecSMiri Korenblit 		return ret;
464d1e879ecSMiri Korenblit 
465d1e879ecSMiri Korenblit 	ret = iwl_mld_send_bt_init_conf(mld);
466d1e879ecSMiri Korenblit 	if (ret)
467d1e879ecSMiri Korenblit 		return ret;
468d1e879ecSMiri Korenblit 
469d1e879ecSMiri Korenblit 	ret = iwl_set_soc_latency(&mld->fwrt);
470d1e879ecSMiri Korenblit 	if (ret)
471d1e879ecSMiri Korenblit 		return ret;
472d1e879ecSMiri Korenblit 
473d1e879ecSMiri Korenblit 	iwl_mld_configure_lari(mld);
474d1e879ecSMiri Korenblit 
475d1e879ecSMiri Korenblit 	ret = iwl_mld_config_temp_report_ths(mld);
476d1e879ecSMiri Korenblit 	if (ret)
477d1e879ecSMiri Korenblit 		return ret;
478d1e879ecSMiri Korenblit 
479d1e879ecSMiri Korenblit #ifdef CONFIG_THERMAL
480d1e879ecSMiri Korenblit 	ret = iwl_mld_config_ctdp(mld, mld->cooling_dev.cur_state,
481d1e879ecSMiri Korenblit 				  CTDP_CMD_OPERATION_START);
482d1e879ecSMiri Korenblit 	if (ret)
483d1e879ecSMiri Korenblit 		return ret;
484d1e879ecSMiri Korenblit #endif
485d1e879ecSMiri Korenblit 
486d1e879ecSMiri Korenblit 	ret = iwl_configure_rxq(&mld->fwrt);
487d1e879ecSMiri Korenblit 	if (ret)
488d1e879ecSMiri Korenblit 		return ret;
489d1e879ecSMiri Korenblit 
490d1e879ecSMiri Korenblit 	ret = iwl_mld_send_rss_cfg_cmd(mld);
491d1e879ecSMiri Korenblit 	if (ret)
492d1e879ecSMiri Korenblit 		return ret;
493d1e879ecSMiri Korenblit 
494d1e879ecSMiri Korenblit 	ret = iwl_mld_config_scan(mld);
495d1e879ecSMiri Korenblit 	if (ret)
496d1e879ecSMiri Korenblit 		return ret;
497d1e879ecSMiri Korenblit 
498d1e879ecSMiri Korenblit 	ret = iwl_mld_update_device_power(mld, false);
499d1e879ecSMiri Korenblit 	if (ret)
500d1e879ecSMiri Korenblit 		return ret;
501d1e879ecSMiri Korenblit 
502d1e879ecSMiri Korenblit 	if (mld->fw_status.in_hw_restart) {
503d1e879ecSMiri Korenblit 		iwl_mld_send_recovery_cmd(mld, ERROR_RECOVERY_UPDATE_DB);
504d1e879ecSMiri Korenblit 		iwl_mld_time_sync_fw_config(mld);
505d1e879ecSMiri Korenblit 	}
506d1e879ecSMiri Korenblit 
507d1e879ecSMiri Korenblit 	iwl_mld_led_config_fw(mld);
508d1e879ecSMiri Korenblit 
509d1e879ecSMiri Korenblit 	ret = iwl_mld_init_ppag(mld);
510d1e879ecSMiri Korenblit 	if (ret)
511d1e879ecSMiri Korenblit 		return ret;
512d1e879ecSMiri Korenblit 
513d1e879ecSMiri Korenblit 	ret = iwl_mld_init_sar(mld);
514d1e879ecSMiri Korenblit 	if (ret)
515d1e879ecSMiri Korenblit 		return ret;
516d1e879ecSMiri Korenblit 
517d1e879ecSMiri Korenblit 	ret = iwl_mld_init_sgom(mld);
518d1e879ecSMiri Korenblit 	if (ret)
519d1e879ecSMiri Korenblit 		return ret;
520d1e879ecSMiri Korenblit 
521d1e879ecSMiri Korenblit 	iwl_mld_init_tas(mld);
522d1e879ecSMiri Korenblit 	iwl_mld_init_uats(mld);
523d1e879ecSMiri Korenblit 
524d1e879ecSMiri Korenblit 	return 0;
525d1e879ecSMiri Korenblit }
526d1e879ecSMiri Korenblit 
iwl_mld_start_fw(struct iwl_mld * mld)527d1e879ecSMiri Korenblit int iwl_mld_start_fw(struct iwl_mld *mld)
528d1e879ecSMiri Korenblit {
529d1e879ecSMiri Korenblit 	int ret;
530d1e879ecSMiri Korenblit 
531d1e879ecSMiri Korenblit 	lockdep_assert_wiphy(mld->wiphy);
532d1e879ecSMiri Korenblit 
533d1e879ecSMiri Korenblit 	ret = iwl_mld_load_fw(mld);
534d1e879ecSMiri Korenblit 	if (IWL_FW_CHECK(mld, ret, "Failed to start firmware %d\n", ret)) {
535d1e879ecSMiri Korenblit 		iwl_fw_dbg_error_collect(&mld->fwrt, FW_DBG_TRIGGER_DRIVER);
536e1d35eabSMiri Korenblit 		return ret;
537d1e879ecSMiri Korenblit 	}
538d1e879ecSMiri Korenblit 
539d1e879ecSMiri Korenblit 	IWL_DEBUG_INFO(mld, "uCode started.\n");
540d1e879ecSMiri Korenblit 
541d1e879ecSMiri Korenblit 	ret = iwl_mld_config_fw(mld);
542d1e879ecSMiri Korenblit 	if (ret)
543d1e879ecSMiri Korenblit 		goto error;
544d1e879ecSMiri Korenblit 
545*f81aa834SIlan Peer 	ret = iwl_mld_init_mcc(mld);
546*f81aa834SIlan Peer 	if (ret)
547*f81aa834SIlan Peer 		goto error;
548*f81aa834SIlan Peer 
549d1e879ecSMiri Korenblit 	return 0;
550d1e879ecSMiri Korenblit 
551d1e879ecSMiri Korenblit error:
552d1e879ecSMiri Korenblit 	iwl_mld_stop_fw(mld);
553d1e879ecSMiri Korenblit 	return ret;
554d1e879ecSMiri Korenblit }
555