xref: /freebsd/sys/contrib/dev/iwlwifi/mvm/fw.c (revision a4128aad8503277614f2d214011ef60a19447b83)
1bfcc09ddSBjoern A. Zeeb // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2bfcc09ddSBjoern A. Zeeb /*
3*a4128aadSBjoern A. Zeeb  * Copyright (C) 2012-2014, 2018-2024 Intel Corporation
4bfcc09ddSBjoern A. Zeeb  * Copyright (C) 2013-2015 Intel Mobile Communications GmbH
5bfcc09ddSBjoern A. Zeeb  * Copyright (C) 2016-2017 Intel Deutschland GmbH
6bfcc09ddSBjoern A. Zeeb  */
7bfcc09ddSBjoern A. Zeeb #include <net/mac80211.h>
8bfcc09ddSBjoern A. Zeeb #include <linux/netdevice.h>
9bfcc09ddSBjoern A. Zeeb #include <linux/dmi.h>
10bfcc09ddSBjoern A. Zeeb 
11bfcc09ddSBjoern A. Zeeb #include "iwl-trans.h"
12bfcc09ddSBjoern A. Zeeb #include "iwl-op-mode.h"
13bfcc09ddSBjoern A. Zeeb #include "fw/img.h"
14bfcc09ddSBjoern A. Zeeb #include "iwl-debug.h"
15bfcc09ddSBjoern A. Zeeb #include "iwl-prph.h"
16bfcc09ddSBjoern A. Zeeb #include "fw/acpi.h"
17bfcc09ddSBjoern A. Zeeb #include "fw/pnvm.h"
18*a4128aadSBjoern A. Zeeb #include "fw/uefi.h"
19*a4128aadSBjoern A. Zeeb #include "fw/regulatory.h"
20bfcc09ddSBjoern A. Zeeb 
21bfcc09ddSBjoern A. Zeeb #include "mvm.h"
22bfcc09ddSBjoern A. Zeeb #include "fw/dbg.h"
23bfcc09ddSBjoern A. Zeeb #include "iwl-phy-db.h"
24bfcc09ddSBjoern A. Zeeb #include "iwl-modparams.h"
25bfcc09ddSBjoern A. Zeeb #include "iwl-nvm-parse.h"
269af1bba4SBjoern A. Zeeb #include "time-sync.h"
27bfcc09ddSBjoern A. Zeeb 
28*a4128aadSBjoern A. Zeeb #define MVM_UCODE_ALIVE_TIMEOUT	(2 * HZ)
29bfcc09ddSBjoern A. Zeeb #define MVM_UCODE_CALIB_TIMEOUT	(2 * HZ)
30bfcc09ddSBjoern A. Zeeb 
31bfcc09ddSBjoern A. Zeeb struct iwl_mvm_alive_data {
32bfcc09ddSBjoern A. Zeeb 	bool valid;
33bfcc09ddSBjoern A. Zeeb 	u32 scd_base_addr;
34bfcc09ddSBjoern A. Zeeb };
35bfcc09ddSBjoern A. Zeeb 
36bfcc09ddSBjoern A. Zeeb static int iwl_send_tx_ant_cfg(struct iwl_mvm *mvm, u8 valid_tx_ant)
37bfcc09ddSBjoern A. Zeeb {
38bfcc09ddSBjoern A. Zeeb 	struct iwl_tx_ant_cfg_cmd tx_ant_cmd = {
39bfcc09ddSBjoern A. Zeeb 		.valid = cpu_to_le32(valid_tx_ant),
40bfcc09ddSBjoern A. Zeeb 	};
41bfcc09ddSBjoern A. Zeeb 
42bfcc09ddSBjoern A. Zeeb 	IWL_DEBUG_FW(mvm, "select valid tx ant: %u\n", valid_tx_ant);
43bfcc09ddSBjoern A. Zeeb 	return iwl_mvm_send_cmd_pdu(mvm, TX_ANT_CONFIGURATION_CMD, 0,
44bfcc09ddSBjoern A. Zeeb 				    sizeof(tx_ant_cmd), &tx_ant_cmd);
45bfcc09ddSBjoern A. Zeeb }
46bfcc09ddSBjoern A. Zeeb 
47bfcc09ddSBjoern A. Zeeb static int iwl_send_rss_cfg_cmd(struct iwl_mvm *mvm)
48bfcc09ddSBjoern A. Zeeb {
49bfcc09ddSBjoern A. Zeeb 	int i;
50bfcc09ddSBjoern A. Zeeb 	struct iwl_rss_config_cmd cmd = {
51bfcc09ddSBjoern A. Zeeb 		.flags = cpu_to_le32(IWL_RSS_ENABLE),
52bfcc09ddSBjoern A. Zeeb 		.hash_mask = BIT(IWL_RSS_HASH_TYPE_IPV4_TCP) |
53bfcc09ddSBjoern A. Zeeb 			     BIT(IWL_RSS_HASH_TYPE_IPV4_UDP) |
54bfcc09ddSBjoern A. Zeeb 			     BIT(IWL_RSS_HASH_TYPE_IPV4_PAYLOAD) |
55bfcc09ddSBjoern A. Zeeb 			     BIT(IWL_RSS_HASH_TYPE_IPV6_TCP) |
56bfcc09ddSBjoern A. Zeeb 			     BIT(IWL_RSS_HASH_TYPE_IPV6_UDP) |
57bfcc09ddSBjoern A. Zeeb 			     BIT(IWL_RSS_HASH_TYPE_IPV6_PAYLOAD),
58bfcc09ddSBjoern A. Zeeb 	};
59bfcc09ddSBjoern A. Zeeb 
60bfcc09ddSBjoern A. Zeeb 	if (mvm->trans->num_rx_queues == 1)
61bfcc09ddSBjoern A. Zeeb 		return 0;
62bfcc09ddSBjoern A. Zeeb 
63bfcc09ddSBjoern A. Zeeb 	/* Do not direct RSS traffic to Q 0 which is our fallback queue */
64bfcc09ddSBjoern A. Zeeb 	for (i = 0; i < ARRAY_SIZE(cmd.indirection_table); i++)
65bfcc09ddSBjoern A. Zeeb 		cmd.indirection_table[i] =
66bfcc09ddSBjoern A. Zeeb 			1 + (i % (mvm->trans->num_rx_queues - 1));
67bfcc09ddSBjoern A. Zeeb 	netdev_rss_key_fill(cmd.secret_key, sizeof(cmd.secret_key));
68bfcc09ddSBjoern A. Zeeb 
69bfcc09ddSBjoern A. Zeeb 	return iwl_mvm_send_cmd_pdu(mvm, RSS_CONFIG_CMD, 0, sizeof(cmd), &cmd);
70bfcc09ddSBjoern A. Zeeb }
71bfcc09ddSBjoern A. Zeeb 
72bfcc09ddSBjoern A. Zeeb static int iwl_mvm_send_dqa_cmd(struct iwl_mvm *mvm)
73bfcc09ddSBjoern A. Zeeb {
74bfcc09ddSBjoern A. Zeeb 	struct iwl_dqa_enable_cmd dqa_cmd = {
75bfcc09ddSBjoern A. Zeeb 		.cmd_queue = cpu_to_le32(IWL_MVM_DQA_CMD_QUEUE),
76bfcc09ddSBjoern A. Zeeb 	};
77d9836fb4SBjoern A. Zeeb 	u32 cmd_id = WIDE_ID(DATA_PATH_GROUP, DQA_ENABLE_CMD);
78bfcc09ddSBjoern A. Zeeb 	int ret;
79bfcc09ddSBjoern A. Zeeb 
80bfcc09ddSBjoern A. Zeeb 	ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, sizeof(dqa_cmd), &dqa_cmd);
81bfcc09ddSBjoern A. Zeeb 	if (ret)
82bfcc09ddSBjoern A. Zeeb 		IWL_ERR(mvm, "Failed to send DQA enabling command: %d\n", ret);
83bfcc09ddSBjoern A. Zeeb 	else
84bfcc09ddSBjoern A. Zeeb 		IWL_DEBUG_FW(mvm, "Working in DQA mode\n");
85bfcc09ddSBjoern A. Zeeb 
86bfcc09ddSBjoern A. Zeeb 	return ret;
87bfcc09ddSBjoern A. Zeeb }
88bfcc09ddSBjoern A. Zeeb 
89bfcc09ddSBjoern A. Zeeb void iwl_mvm_mfu_assert_dump_notif(struct iwl_mvm *mvm,
90bfcc09ddSBjoern A. Zeeb 				   struct iwl_rx_cmd_buffer *rxb)
91bfcc09ddSBjoern A. Zeeb {
92bfcc09ddSBjoern A. Zeeb 	struct iwl_rx_packet *pkt = rxb_addr(rxb);
93bfcc09ddSBjoern A. Zeeb 	struct iwl_mfu_assert_dump_notif *mfu_dump_notif = (void *)pkt->data;
94bfcc09ddSBjoern A. Zeeb 
95bfcc09ddSBjoern A. Zeeb 	if (mfu_dump_notif->index_num == 0)
96bfcc09ddSBjoern A. Zeeb 		IWL_INFO(mvm, "MFUART assert id 0x%x occurred\n",
97bfcc09ddSBjoern A. Zeeb 			 le32_to_cpu(mfu_dump_notif->assert_id));
98bfcc09ddSBjoern A. Zeeb }
99bfcc09ddSBjoern A. Zeeb 
100bfcc09ddSBjoern A. Zeeb static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait,
101bfcc09ddSBjoern A. Zeeb 			 struct iwl_rx_packet *pkt, void *data)
102bfcc09ddSBjoern A. Zeeb {
103bfcc09ddSBjoern A. Zeeb 	unsigned int pkt_len = iwl_rx_packet_payload_len(pkt);
104bfcc09ddSBjoern A. Zeeb 	struct iwl_mvm *mvm =
105bfcc09ddSBjoern A. Zeeb 		container_of(notif_wait, struct iwl_mvm, notif_wait);
106bfcc09ddSBjoern A. Zeeb 	struct iwl_mvm_alive_data *alive_data = data;
107bfcc09ddSBjoern A. Zeeb 	struct iwl_umac_alive *umac;
108bfcc09ddSBjoern A. Zeeb 	struct iwl_lmac_alive *lmac1;
109bfcc09ddSBjoern A. Zeeb 	struct iwl_lmac_alive *lmac2 = NULL;
110bfcc09ddSBjoern A. Zeeb 	u16 status;
111bfcc09ddSBjoern A. Zeeb 	u32 lmac_error_event_table, umac_error_table;
112d9836fb4SBjoern A. Zeeb 	u32 version = iwl_fw_lookup_notif_ver(mvm->fw, LEGACY_GROUP,
113d9836fb4SBjoern A. Zeeb 					      UCODE_ALIVE_NTFY, 0);
114fac1f593SBjoern A. Zeeb 	u32 i;
115bfcc09ddSBjoern A. Zeeb 
1169af1bba4SBjoern A. Zeeb 
117d9836fb4SBjoern A. Zeeb 	if (version == 6) {
118d9836fb4SBjoern A. Zeeb 		struct iwl_alive_ntf_v6 *palive;
119d9836fb4SBjoern A. Zeeb 
120d9836fb4SBjoern A. Zeeb 		if (pkt_len < sizeof(*palive))
121d9836fb4SBjoern A. Zeeb 			return false;
122d9836fb4SBjoern A. Zeeb 
123d9836fb4SBjoern A. Zeeb 		palive = (void *)pkt->data;
124d9836fb4SBjoern A. Zeeb 		mvm->trans->dbg.imr_data.imr_enable =
125d9836fb4SBjoern A. Zeeb 			le32_to_cpu(palive->imr.enabled);
126d9836fb4SBjoern A. Zeeb 		mvm->trans->dbg.imr_data.imr_size =
127d9836fb4SBjoern A. Zeeb 			le32_to_cpu(palive->imr.size);
128d9836fb4SBjoern A. Zeeb 		mvm->trans->dbg.imr_data.imr2sram_remainbyte =
129d9836fb4SBjoern A. Zeeb 			mvm->trans->dbg.imr_data.imr_size;
130d9836fb4SBjoern A. Zeeb 		mvm->trans->dbg.imr_data.imr_base_addr =
131d9836fb4SBjoern A. Zeeb 			palive->imr.base_addr;
132d9836fb4SBjoern A. Zeeb 		mvm->trans->dbg.imr_data.imr_curr_addr =
133d9836fb4SBjoern A. Zeeb 			le64_to_cpu(mvm->trans->dbg.imr_data.imr_base_addr);
134d9836fb4SBjoern A. Zeeb 		IWL_DEBUG_FW(mvm, "IMR Enabled: 0x0%x  size 0x0%x Address 0x%016llx\n",
135d9836fb4SBjoern A. Zeeb 			     mvm->trans->dbg.imr_data.imr_enable,
136d9836fb4SBjoern A. Zeeb 			     mvm->trans->dbg.imr_data.imr_size,
137d9836fb4SBjoern A. Zeeb 			     le64_to_cpu(mvm->trans->dbg.imr_data.imr_base_addr));
138fac1f593SBjoern A. Zeeb 
139fac1f593SBjoern A. Zeeb 		if (!mvm->trans->dbg.imr_data.imr_enable) {
140fac1f593SBjoern A. Zeeb 			for (i = 0; i < ARRAY_SIZE(mvm->trans->dbg.active_regions); i++) {
141fac1f593SBjoern A. Zeeb 				struct iwl_ucode_tlv *reg_tlv;
142fac1f593SBjoern A. Zeeb 				struct iwl_fw_ini_region_tlv *reg;
143fac1f593SBjoern A. Zeeb 
144fac1f593SBjoern A. Zeeb 				reg_tlv = mvm->trans->dbg.active_regions[i];
145fac1f593SBjoern A. Zeeb 				if (!reg_tlv)
146fac1f593SBjoern A. Zeeb 					continue;
147fac1f593SBjoern A. Zeeb 
148fac1f593SBjoern A. Zeeb 				reg = (void *)reg_tlv->data;
149fac1f593SBjoern A. Zeeb 				/*
150fac1f593SBjoern A. Zeeb 				 * We have only one DRAM IMR region, so we
151fac1f593SBjoern A. Zeeb 				 * can break as soon as we find the first
152fac1f593SBjoern A. Zeeb 				 * one.
153fac1f593SBjoern A. Zeeb 				 */
154fac1f593SBjoern A. Zeeb 				if (reg->type == IWL_FW_INI_REGION_DRAM_IMR) {
155fac1f593SBjoern A. Zeeb 					mvm->trans->dbg.unsupported_region_msk |= BIT(i);
156fac1f593SBjoern A. Zeeb 					break;
157fac1f593SBjoern A. Zeeb 				}
158fac1f593SBjoern A. Zeeb 			}
159fac1f593SBjoern A. Zeeb 		}
160d9836fb4SBjoern A. Zeeb 	}
161d9836fb4SBjoern A. Zeeb 
162d9836fb4SBjoern A. Zeeb 	if (version >= 5) {
163bfcc09ddSBjoern A. Zeeb 		struct iwl_alive_ntf_v5 *palive;
164bfcc09ddSBjoern A. Zeeb 
165bfcc09ddSBjoern A. Zeeb 		if (pkt_len < sizeof(*palive))
166bfcc09ddSBjoern A. Zeeb 			return false;
167bfcc09ddSBjoern A. Zeeb 
168bfcc09ddSBjoern A. Zeeb 		palive = (void *)pkt->data;
169bfcc09ddSBjoern A. Zeeb 		umac = &palive->umac_data;
170bfcc09ddSBjoern A. Zeeb 		lmac1 = &palive->lmac_data[0];
171bfcc09ddSBjoern A. Zeeb 		lmac2 = &palive->lmac_data[1];
172bfcc09ddSBjoern A. Zeeb 		status = le16_to_cpu(palive->status);
173bfcc09ddSBjoern A. Zeeb 
174bfcc09ddSBjoern A. Zeeb 		mvm->trans->sku_id[0] = le32_to_cpu(palive->sku_id.data[0]);
175bfcc09ddSBjoern A. Zeeb 		mvm->trans->sku_id[1] = le32_to_cpu(palive->sku_id.data[1]);
176bfcc09ddSBjoern A. Zeeb 		mvm->trans->sku_id[2] = le32_to_cpu(palive->sku_id.data[2]);
177bfcc09ddSBjoern A. Zeeb 
178bfcc09ddSBjoern A. Zeeb 		IWL_DEBUG_FW(mvm, "Got sku_id: 0x0%x 0x0%x 0x0%x\n",
179bfcc09ddSBjoern A. Zeeb 			     mvm->trans->sku_id[0],
180bfcc09ddSBjoern A. Zeeb 			     mvm->trans->sku_id[1],
181bfcc09ddSBjoern A. Zeeb 			     mvm->trans->sku_id[2]);
182bfcc09ddSBjoern A. Zeeb 	} else if (iwl_rx_packet_payload_len(pkt) == sizeof(struct iwl_alive_ntf_v4)) {
183bfcc09ddSBjoern A. Zeeb 		struct iwl_alive_ntf_v4 *palive;
184bfcc09ddSBjoern A. Zeeb 
185bfcc09ddSBjoern A. Zeeb 		if (pkt_len < sizeof(*palive))
186bfcc09ddSBjoern A. Zeeb 			return false;
187bfcc09ddSBjoern A. Zeeb 
188bfcc09ddSBjoern A. Zeeb 		palive = (void *)pkt->data;
189bfcc09ddSBjoern A. Zeeb 		umac = &palive->umac_data;
190bfcc09ddSBjoern A. Zeeb 		lmac1 = &palive->lmac_data[0];
191bfcc09ddSBjoern A. Zeeb 		lmac2 = &palive->lmac_data[1];
192bfcc09ddSBjoern A. Zeeb 		status = le16_to_cpu(palive->status);
193bfcc09ddSBjoern A. Zeeb 	} else if (iwl_rx_packet_payload_len(pkt) ==
194bfcc09ddSBjoern A. Zeeb 		   sizeof(struct iwl_alive_ntf_v3)) {
195bfcc09ddSBjoern A. Zeeb 		struct iwl_alive_ntf_v3 *palive3;
196bfcc09ddSBjoern A. Zeeb 
197bfcc09ddSBjoern A. Zeeb 		if (pkt_len < sizeof(*palive3))
198bfcc09ddSBjoern A. Zeeb 			return false;
199bfcc09ddSBjoern A. Zeeb 
200bfcc09ddSBjoern A. Zeeb 		palive3 = (void *)pkt->data;
201bfcc09ddSBjoern A. Zeeb 		umac = &palive3->umac_data;
202bfcc09ddSBjoern A. Zeeb 		lmac1 = &palive3->lmac_data;
203bfcc09ddSBjoern A. Zeeb 		status = le16_to_cpu(palive3->status);
204bfcc09ddSBjoern A. Zeeb 	} else {
205bfcc09ddSBjoern A. Zeeb 		WARN(1, "unsupported alive notification (size %d)\n",
206bfcc09ddSBjoern A. Zeeb 		     iwl_rx_packet_payload_len(pkt));
207bfcc09ddSBjoern A. Zeeb 		/* get timeout later */
208bfcc09ddSBjoern A. Zeeb 		return false;
209bfcc09ddSBjoern A. Zeeb 	}
210bfcc09ddSBjoern A. Zeeb 
211bfcc09ddSBjoern A. Zeeb 	lmac_error_event_table =
212bfcc09ddSBjoern A. Zeeb 		le32_to_cpu(lmac1->dbg_ptrs.error_event_table_ptr);
213bfcc09ddSBjoern A. Zeeb 	iwl_fw_lmac1_set_alive_err_table(mvm->trans, lmac_error_event_table);
214bfcc09ddSBjoern A. Zeeb 
215bfcc09ddSBjoern A. Zeeb 	if (lmac2)
216bfcc09ddSBjoern A. Zeeb 		mvm->trans->dbg.lmac_error_event_table[1] =
217bfcc09ddSBjoern A. Zeeb 			le32_to_cpu(lmac2->dbg_ptrs.error_event_table_ptr);
218bfcc09ddSBjoern A. Zeeb 
219bfcc09ddSBjoern A. Zeeb 	umac_error_table = le32_to_cpu(umac->dbg_ptrs.error_info_addr) &
220bfcc09ddSBjoern A. Zeeb 							~FW_ADDR_CACHE_CONTROL;
221bfcc09ddSBjoern A. Zeeb 
222bfcc09ddSBjoern A. Zeeb 	if (umac_error_table) {
223bfcc09ddSBjoern A. Zeeb 		if (umac_error_table >=
224bfcc09ddSBjoern A. Zeeb 		    mvm->trans->cfg->min_umac_error_event_table) {
225bfcc09ddSBjoern A. Zeeb 			iwl_fw_umac_set_alive_err_table(mvm->trans,
226bfcc09ddSBjoern A. Zeeb 							umac_error_table);
227bfcc09ddSBjoern A. Zeeb 		} else {
228bfcc09ddSBjoern A. Zeeb 			IWL_ERR(mvm,
229bfcc09ddSBjoern A. Zeeb 				"Not valid error log pointer 0x%08X for %s uCode\n",
230bfcc09ddSBjoern A. Zeeb 				umac_error_table,
231bfcc09ddSBjoern A. Zeeb 				(mvm->fwrt.cur_fw_img == IWL_UCODE_INIT) ?
232bfcc09ddSBjoern A. Zeeb 				"Init" : "RT");
233bfcc09ddSBjoern A. Zeeb 		}
234bfcc09ddSBjoern A. Zeeb 	}
235bfcc09ddSBjoern A. Zeeb 
236bfcc09ddSBjoern A. Zeeb 	alive_data->scd_base_addr = le32_to_cpu(lmac1->dbg_ptrs.scd_base_ptr);
237bfcc09ddSBjoern A. Zeeb 	alive_data->valid = status == IWL_ALIVE_STATUS_OK;
238bfcc09ddSBjoern A. Zeeb 
239bfcc09ddSBjoern A. Zeeb 	IWL_DEBUG_FW(mvm,
240bfcc09ddSBjoern A. Zeeb 		     "Alive ucode status 0x%04x revision 0x%01X 0x%01X\n",
241bfcc09ddSBjoern A. Zeeb 		     status, lmac1->ver_type, lmac1->ver_subtype);
242bfcc09ddSBjoern A. Zeeb 
243bfcc09ddSBjoern A. Zeeb 	if (lmac2)
244bfcc09ddSBjoern A. Zeeb 		IWL_DEBUG_FW(mvm, "Alive ucode CDB\n");
245bfcc09ddSBjoern A. Zeeb 
246bfcc09ddSBjoern A. Zeeb 	IWL_DEBUG_FW(mvm,
247bfcc09ddSBjoern A. Zeeb 		     "UMAC version: Major - 0x%x, Minor - 0x%x\n",
248bfcc09ddSBjoern A. Zeeb 		     le32_to_cpu(umac->umac_major),
249bfcc09ddSBjoern A. Zeeb 		     le32_to_cpu(umac->umac_minor));
250bfcc09ddSBjoern A. Zeeb 
251bfcc09ddSBjoern A. Zeeb 	iwl_fwrt_update_fw_versions(&mvm->fwrt, lmac1, umac);
252bfcc09ddSBjoern A. Zeeb 
253bfcc09ddSBjoern A. Zeeb 	return true;
254bfcc09ddSBjoern A. Zeeb }
255bfcc09ddSBjoern A. Zeeb 
256bfcc09ddSBjoern A. Zeeb static bool iwl_wait_init_complete(struct iwl_notif_wait_data *notif_wait,
257bfcc09ddSBjoern A. Zeeb 				   struct iwl_rx_packet *pkt, void *data)
258bfcc09ddSBjoern A. Zeeb {
259bfcc09ddSBjoern A. Zeeb 	WARN_ON(pkt->hdr.cmd != INIT_COMPLETE_NOTIF);
260bfcc09ddSBjoern A. Zeeb 
261bfcc09ddSBjoern A. Zeeb 	return true;
262bfcc09ddSBjoern A. Zeeb }
263bfcc09ddSBjoern A. Zeeb 
264bfcc09ddSBjoern A. Zeeb static bool iwl_wait_phy_db_entry(struct iwl_notif_wait_data *notif_wait,
265bfcc09ddSBjoern A. Zeeb 				  struct iwl_rx_packet *pkt, void *data)
266bfcc09ddSBjoern A. Zeeb {
267bfcc09ddSBjoern A. Zeeb 	struct iwl_phy_db *phy_db = data;
268bfcc09ddSBjoern A. Zeeb 
269bfcc09ddSBjoern A. Zeeb 	if (pkt->hdr.cmd != CALIB_RES_NOTIF_PHY_DB) {
270bfcc09ddSBjoern A. Zeeb 		WARN_ON(pkt->hdr.cmd != INIT_COMPLETE_NOTIF);
271bfcc09ddSBjoern A. Zeeb 		return true;
272bfcc09ddSBjoern A. Zeeb 	}
273bfcc09ddSBjoern A. Zeeb 
274bfcc09ddSBjoern A. Zeeb 	WARN_ON(iwl_phy_db_set_section(phy_db, pkt));
275bfcc09ddSBjoern A. Zeeb 
276bfcc09ddSBjoern A. Zeeb 	return false;
277bfcc09ddSBjoern A. Zeeb }
278bfcc09ddSBjoern A. Zeeb 
279d9836fb4SBjoern A. Zeeb static void iwl_mvm_print_pd_notification(struct iwl_mvm *mvm)
280d9836fb4SBjoern A. Zeeb {
2819af1bba4SBjoern A. Zeeb #define IWL_FW_PRINT_REG_INFO(reg_name) \
2829af1bba4SBjoern A. Zeeb 	IWL_ERR(mvm, #reg_name ": 0x%x\n", iwl_read_umac_prph(trans, reg_name))
2839af1bba4SBjoern A. Zeeb 
284d9836fb4SBjoern A. Zeeb 	struct iwl_trans *trans = mvm->trans;
285d9836fb4SBjoern A. Zeeb 	enum iwl_device_family device_family = trans->trans_cfg->device_family;
286d9836fb4SBjoern A. Zeeb 
287d9836fb4SBjoern A. Zeeb 	if (device_family < IWL_DEVICE_FAMILY_8000)
288d9836fb4SBjoern A. Zeeb 		return;
289d9836fb4SBjoern A. Zeeb 
290d9836fb4SBjoern A. Zeeb 	if (device_family <= IWL_DEVICE_FAMILY_9000)
2919af1bba4SBjoern A. Zeeb 		IWL_FW_PRINT_REG_INFO(WFPM_ARC1_PD_NOTIFICATION);
292d9836fb4SBjoern A. Zeeb 	else
2939af1bba4SBjoern A. Zeeb 		IWL_FW_PRINT_REG_INFO(WFPM_LMAC1_PD_NOTIFICATION);
294d9836fb4SBjoern A. Zeeb 
2959af1bba4SBjoern A. Zeeb 	IWL_FW_PRINT_REG_INFO(HPM_SECONDARY_DEVICE_STATE);
296d9836fb4SBjoern A. Zeeb 
2979af1bba4SBjoern A. Zeeb 	/* print OPT info */
2989af1bba4SBjoern A. Zeeb 	IWL_FW_PRINT_REG_INFO(WFPM_MAC_OTP_CFG7_ADDR);
2999af1bba4SBjoern A. Zeeb 	IWL_FW_PRINT_REG_INFO(WFPM_MAC_OTP_CFG7_DATA);
300d9836fb4SBjoern A. Zeeb }
301d9836fb4SBjoern A. Zeeb 
302bfcc09ddSBjoern A. Zeeb static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm,
303bfcc09ddSBjoern A. Zeeb 					 enum iwl_ucode_type ucode_type)
304bfcc09ddSBjoern A. Zeeb {
305bfcc09ddSBjoern A. Zeeb 	struct iwl_notification_wait alive_wait;
306bfcc09ddSBjoern A. Zeeb 	struct iwl_mvm_alive_data alive_data = {};
307bfcc09ddSBjoern A. Zeeb 	const struct fw_img *fw;
308bfcc09ddSBjoern A. Zeeb 	int ret;
309bfcc09ddSBjoern A. Zeeb 	enum iwl_ucode_type old_type = mvm->fwrt.cur_fw_img;
310bfcc09ddSBjoern A. Zeeb 	static const u16 alive_cmd[] = { UCODE_ALIVE_NTFY };
311bfcc09ddSBjoern A. Zeeb 	bool run_in_rfkill =
312bfcc09ddSBjoern A. Zeeb 		ucode_type == IWL_UCODE_INIT || iwl_mvm_has_unified_ucode(mvm);
3139af1bba4SBjoern A. Zeeb 	u8 count;
3149af1bba4SBjoern A. Zeeb 	struct iwl_pc_data *pc_data;
315bfcc09ddSBjoern A. Zeeb 
316bfcc09ddSBjoern A. Zeeb 	if (ucode_type == IWL_UCODE_REGULAR &&
317bfcc09ddSBjoern A. Zeeb 	    iwl_fw_dbg_conf_usniffer(mvm->fw, FW_DBG_START_FROM_ALIVE) &&
318bfcc09ddSBjoern A. Zeeb 	    !(fw_has_capa(&mvm->fw->ucode_capa,
319bfcc09ddSBjoern A. Zeeb 			  IWL_UCODE_TLV_CAPA_USNIFFER_UNIFIED)))
320bfcc09ddSBjoern A. Zeeb 		fw = iwl_get_ucode_image(mvm->fw, IWL_UCODE_REGULAR_USNIFFER);
321bfcc09ddSBjoern A. Zeeb 	else
322bfcc09ddSBjoern A. Zeeb 		fw = iwl_get_ucode_image(mvm->fw, ucode_type);
323bfcc09ddSBjoern A. Zeeb 	if (WARN_ON(!fw))
324bfcc09ddSBjoern A. Zeeb 		return -EINVAL;
325bfcc09ddSBjoern A. Zeeb 	iwl_fw_set_current_image(&mvm->fwrt, ucode_type);
326bfcc09ddSBjoern A. Zeeb 	clear_bit(IWL_MVM_STATUS_FIRMWARE_RUNNING, &mvm->status);
327bfcc09ddSBjoern A. Zeeb 
328bfcc09ddSBjoern A. Zeeb 	iwl_init_notification_wait(&mvm->notif_wait, &alive_wait,
329bfcc09ddSBjoern A. Zeeb 				   alive_cmd, ARRAY_SIZE(alive_cmd),
330bfcc09ddSBjoern A. Zeeb 				   iwl_alive_fn, &alive_data);
331bfcc09ddSBjoern A. Zeeb 
332bfcc09ddSBjoern A. Zeeb 	/*
333bfcc09ddSBjoern A. Zeeb 	 * We want to load the INIT firmware even in RFKILL
334bfcc09ddSBjoern A. Zeeb 	 * For the unified firmware case, the ucode_type is not
335bfcc09ddSBjoern A. Zeeb 	 * INIT, but we still need to run it.
336bfcc09ddSBjoern A. Zeeb 	 */
337bfcc09ddSBjoern A. Zeeb 	ret = iwl_trans_start_fw(mvm->trans, fw, run_in_rfkill);
338bfcc09ddSBjoern A. Zeeb 	if (ret) {
339bfcc09ddSBjoern A. Zeeb 		iwl_fw_set_current_image(&mvm->fwrt, old_type);
340bfcc09ddSBjoern A. Zeeb 		iwl_remove_notification(&mvm->notif_wait, &alive_wait);
341bfcc09ddSBjoern A. Zeeb 		return ret;
342bfcc09ddSBjoern A. Zeeb 	}
343bfcc09ddSBjoern A. Zeeb 
344bfcc09ddSBjoern A. Zeeb 	/*
345bfcc09ddSBjoern A. Zeeb 	 * Some things may run in the background now, but we
346bfcc09ddSBjoern A. Zeeb 	 * just wait for the ALIVE notification here.
347bfcc09ddSBjoern A. Zeeb 	 */
348bfcc09ddSBjoern A. Zeeb 	ret = iwl_wait_notification(&mvm->notif_wait, &alive_wait,
349bfcc09ddSBjoern A. Zeeb 				    MVM_UCODE_ALIVE_TIMEOUT);
3509af1bba4SBjoern A. Zeeb 
3519af1bba4SBjoern A. Zeeb 	if (mvm->trans->trans_cfg->device_family ==
3529af1bba4SBjoern A. Zeeb 	    IWL_DEVICE_FAMILY_AX210) {
3539af1bba4SBjoern A. Zeeb 		/* print these registers regardless of alive fail/success */
3549af1bba4SBjoern A. Zeeb 		IWL_INFO(mvm, "WFPM_UMAC_PD_NOTIFICATION: 0x%x\n",
3559af1bba4SBjoern A. Zeeb 			 iwl_read_umac_prph(mvm->trans, WFPM_ARC1_PD_NOTIFICATION));
3569af1bba4SBjoern A. Zeeb 		IWL_INFO(mvm, "WFPM_LMAC2_PD_NOTIFICATION: 0x%x\n",
3579af1bba4SBjoern A. Zeeb 			 iwl_read_umac_prph(mvm->trans, WFPM_LMAC2_PD_NOTIFICATION));
3589af1bba4SBjoern A. Zeeb 		IWL_INFO(mvm, "WFPM_AUTH_KEY_0: 0x%x\n",
3599af1bba4SBjoern A. Zeeb 			 iwl_read_umac_prph(mvm->trans, SB_MODIFY_CFG_FLAG));
3609af1bba4SBjoern A. Zeeb 		IWL_INFO(mvm, "CNVI_SCU_SEQ_DATA_DW9: 0x%x\n",
3619af1bba4SBjoern A. Zeeb 			 iwl_read_prph(mvm->trans, CNVI_SCU_SEQ_DATA_DW9));
3629af1bba4SBjoern A. Zeeb 	}
3639af1bba4SBjoern A. Zeeb 
364bfcc09ddSBjoern A. Zeeb 	if (ret) {
365bfcc09ddSBjoern A. Zeeb 		struct iwl_trans *trans = mvm->trans;
366bfcc09ddSBjoern A. Zeeb 
367bfcc09ddSBjoern A. Zeeb 		/* SecBoot info */
368bfcc09ddSBjoern A. Zeeb 		if (trans->trans_cfg->device_family >=
369bfcc09ddSBjoern A. Zeeb 					IWL_DEVICE_FAMILY_22000) {
370bfcc09ddSBjoern A. Zeeb 			IWL_ERR(mvm,
371bfcc09ddSBjoern A. Zeeb 				"SecBoot CPU1 Status: 0x%x, CPU2 Status: 0x%x\n",
372bfcc09ddSBjoern A. Zeeb 				iwl_read_umac_prph(trans, UMAG_SB_CPU_1_STATUS),
373bfcc09ddSBjoern A. Zeeb 				iwl_read_umac_prph(trans,
374bfcc09ddSBjoern A. Zeeb 						   UMAG_SB_CPU_2_STATUS));
375bfcc09ddSBjoern A. Zeeb 		} else if (trans->trans_cfg->device_family >=
376bfcc09ddSBjoern A. Zeeb 			   IWL_DEVICE_FAMILY_8000) {
377bfcc09ddSBjoern A. Zeeb 			IWL_ERR(mvm,
378bfcc09ddSBjoern A. Zeeb 				"SecBoot CPU1 Status: 0x%x, CPU2 Status: 0x%x\n",
379bfcc09ddSBjoern A. Zeeb 				iwl_read_prph(trans, SB_CPU_1_STATUS),
380bfcc09ddSBjoern A. Zeeb 				iwl_read_prph(trans, SB_CPU_2_STATUS));
381bfcc09ddSBjoern A. Zeeb 		}
382bfcc09ddSBjoern A. Zeeb 
383d9836fb4SBjoern A. Zeeb 		iwl_mvm_print_pd_notification(mvm);
384d9836fb4SBjoern A. Zeeb 
385bfcc09ddSBjoern A. Zeeb 		/* LMAC/UMAC PC info */
386bfcc09ddSBjoern A. Zeeb 		if (trans->trans_cfg->device_family >=
3879af1bba4SBjoern A. Zeeb 					IWL_DEVICE_FAMILY_22000) {
3889af1bba4SBjoern A. Zeeb 			pc_data = trans->dbg.pc_data;
3899af1bba4SBjoern A. Zeeb 			for (count = 0; count < trans->dbg.num_pc;
3909af1bba4SBjoern A. Zeeb 			     count++, pc_data++)
3919af1bba4SBjoern A. Zeeb 				IWL_ERR(mvm, "%s: 0x%x\n",
3929af1bba4SBjoern A. Zeeb 					pc_data->pc_name,
3939af1bba4SBjoern A. Zeeb 					pc_data->pc_address);
3949af1bba4SBjoern A. Zeeb 		} else if (trans->trans_cfg->device_family >=
395bfcc09ddSBjoern A. Zeeb 					IWL_DEVICE_FAMILY_9000) {
396bfcc09ddSBjoern A. Zeeb 			IWL_ERR(mvm, "UMAC PC: 0x%x\n",
397bfcc09ddSBjoern A. Zeeb 				iwl_read_umac_prph(trans,
398bfcc09ddSBjoern A. Zeeb 						   UREG_UMAC_CURRENT_PC));
399bfcc09ddSBjoern A. Zeeb 			IWL_ERR(mvm, "LMAC PC: 0x%x\n",
400bfcc09ddSBjoern A. Zeeb 				iwl_read_umac_prph(trans,
401bfcc09ddSBjoern A. Zeeb 						   UREG_LMAC1_CURRENT_PC));
402bfcc09ddSBjoern A. Zeeb 			if (iwl_mvm_is_cdb_supported(mvm))
403bfcc09ddSBjoern A. Zeeb 				IWL_ERR(mvm, "LMAC2 PC: 0x%x\n",
404bfcc09ddSBjoern A. Zeeb 					iwl_read_umac_prph(trans,
405bfcc09ddSBjoern A. Zeeb 						UREG_LMAC2_CURRENT_PC));
406bfcc09ddSBjoern A. Zeeb 		}
407bfcc09ddSBjoern A. Zeeb 
408*a4128aadSBjoern A. Zeeb 		if (ret == -ETIMEDOUT && !mvm->fw_product_reset)
409bfcc09ddSBjoern A. Zeeb 			iwl_fw_dbg_error_collect(&mvm->fwrt,
410bfcc09ddSBjoern A. Zeeb 						 FW_DBG_TRIGGER_ALIVE_TIMEOUT);
411bfcc09ddSBjoern A. Zeeb 
412bfcc09ddSBjoern A. Zeeb 		iwl_fw_set_current_image(&mvm->fwrt, old_type);
413bfcc09ddSBjoern A. Zeeb 		return ret;
414bfcc09ddSBjoern A. Zeeb 	}
415bfcc09ddSBjoern A. Zeeb 
416bfcc09ddSBjoern A. Zeeb 	if (!alive_data.valid) {
417bfcc09ddSBjoern A. Zeeb 		IWL_ERR(mvm, "Loaded ucode is not valid!\n");
418bfcc09ddSBjoern A. Zeeb 		iwl_fw_set_current_image(&mvm->fwrt, old_type);
419bfcc09ddSBjoern A. Zeeb 		return -EIO;
420bfcc09ddSBjoern A. Zeeb 	}
421bfcc09ddSBjoern A. Zeeb 
4229af1bba4SBjoern A. Zeeb 	/* if reached this point, Alive notification was received */
4239af1bba4SBjoern A. Zeeb 	iwl_mei_alive_notif(true);
4249af1bba4SBjoern A. Zeeb 
4259af1bba4SBjoern A. Zeeb 	ret = iwl_pnvm_load(mvm->trans, &mvm->notif_wait,
4269af1bba4SBjoern A. Zeeb 			    &mvm->fw->ucode_capa);
427bfcc09ddSBjoern A. Zeeb 	if (ret) {
428bfcc09ddSBjoern A. Zeeb 		IWL_ERR(mvm, "Timeout waiting for PNVM load!\n");
429bfcc09ddSBjoern A. Zeeb 		iwl_fw_set_current_image(&mvm->fwrt, old_type);
430bfcc09ddSBjoern A. Zeeb 		return ret;
431bfcc09ddSBjoern A. Zeeb 	}
432bfcc09ddSBjoern A. Zeeb 
433bfcc09ddSBjoern A. Zeeb 	iwl_trans_fw_alive(mvm->trans, alive_data.scd_base_addr);
434bfcc09ddSBjoern A. Zeeb 
435bfcc09ddSBjoern A. Zeeb 	/*
436bfcc09ddSBjoern A. Zeeb 	 * Note: all the queues are enabled as part of the interface
437bfcc09ddSBjoern A. Zeeb 	 * initialization, but in firmware restart scenarios they
438bfcc09ddSBjoern A. Zeeb 	 * could be stopped, so wake them up. In firmware restart,
439bfcc09ddSBjoern A. Zeeb 	 * mac80211 will have the queues stopped as well until the
440bfcc09ddSBjoern A. Zeeb 	 * reconfiguration completes. During normal startup, they
441bfcc09ddSBjoern A. Zeeb 	 * will be empty.
442bfcc09ddSBjoern A. Zeeb 	 */
443bfcc09ddSBjoern A. Zeeb 
444bfcc09ddSBjoern A. Zeeb 	memset(&mvm->queue_info, 0, sizeof(mvm->queue_info));
445bfcc09ddSBjoern A. Zeeb 	/*
446bfcc09ddSBjoern A. Zeeb 	 * Set a 'fake' TID for the command queue, since we use the
447bfcc09ddSBjoern A. Zeeb 	 * hweight() of the tid_bitmap as a refcount now. Not that
448bfcc09ddSBjoern A. Zeeb 	 * we ever even consider the command queue as one we might
449bfcc09ddSBjoern A. Zeeb 	 * want to reuse, but be safe nevertheless.
450bfcc09ddSBjoern A. Zeeb 	 */
451bfcc09ddSBjoern A. Zeeb 	mvm->queue_info[IWL_MVM_DQA_CMD_QUEUE].tid_bitmap =
452bfcc09ddSBjoern A. Zeeb 		BIT(IWL_MAX_TID_COUNT + 2);
453bfcc09ddSBjoern A. Zeeb 
454bfcc09ddSBjoern A. Zeeb 	set_bit(IWL_MVM_STATUS_FIRMWARE_RUNNING, &mvm->status);
455bfcc09ddSBjoern A. Zeeb #ifdef CONFIG_IWLWIFI_DEBUGFS
456bfcc09ddSBjoern A. Zeeb 	iwl_fw_set_dbg_rec_on(&mvm->fwrt);
457bfcc09ddSBjoern A. Zeeb #endif
458bfcc09ddSBjoern A. Zeeb 
459bfcc09ddSBjoern A. Zeeb 	/*
460*a4128aadSBjoern A. Zeeb 	 * For pre-MLD API (MLD API doesn't use the timestamps):
461bfcc09ddSBjoern A. Zeeb 	 * All the BSSes in the BSS table include the GP2 in the system
462bfcc09ddSBjoern A. Zeeb 	 * at the beacon Rx time, this is of course no longer relevant
463bfcc09ddSBjoern A. Zeeb 	 * since we are resetting the firmware.
464bfcc09ddSBjoern A. Zeeb 	 * Purge all the BSS table.
465bfcc09ddSBjoern A. Zeeb 	 */
466*a4128aadSBjoern A. Zeeb 	if (!mvm->mld_api_is_used)
467bfcc09ddSBjoern A. Zeeb 		cfg80211_bss_flush(mvm->hw->wiphy);
468bfcc09ddSBjoern A. Zeeb 
469bfcc09ddSBjoern A. Zeeb 	return 0;
470bfcc09ddSBjoern A. Zeeb }
471bfcc09ddSBjoern A. Zeeb 
4729af1bba4SBjoern A. Zeeb static void iwl_mvm_phy_filter_init(struct iwl_mvm *mvm,
4739af1bba4SBjoern A. Zeeb 				    struct iwl_phy_specific_cfg *phy_filters)
4749af1bba4SBjoern A. Zeeb {
4759af1bba4SBjoern A. Zeeb #ifdef CONFIG_ACPI
4769af1bba4SBjoern A. Zeeb 	*phy_filters = mvm->phy_filters;
4779af1bba4SBjoern A. Zeeb #endif /* CONFIG_ACPI */
4789af1bba4SBjoern A. Zeeb }
4799af1bba4SBjoern A. Zeeb 
480*a4128aadSBjoern A. Zeeb static void iwl_mvm_uats_init(struct iwl_mvm *mvm)
481*a4128aadSBjoern A. Zeeb {
482*a4128aadSBjoern A. Zeeb 	u8 cmd_ver;
483*a4128aadSBjoern A. Zeeb 	int ret;
484*a4128aadSBjoern A. Zeeb 	struct iwl_host_cmd cmd = {
485*a4128aadSBjoern A. Zeeb 		.id = WIDE_ID(REGULATORY_AND_NVM_GROUP,
486*a4128aadSBjoern A. Zeeb 			      MCC_ALLOWED_AP_TYPE_CMD),
487*a4128aadSBjoern A. Zeeb 		.flags = 0,
488*a4128aadSBjoern A. Zeeb 		.data[0] = &mvm->fwrt.uats_table,
489*a4128aadSBjoern A. Zeeb 		.len[0] =  sizeof(mvm->fwrt.uats_table),
490*a4128aadSBjoern A. Zeeb 		.dataflags[0] = IWL_HCMD_DFL_NOCOPY,
491*a4128aadSBjoern A. Zeeb 	};
492*a4128aadSBjoern A. Zeeb 
493*a4128aadSBjoern A. Zeeb 	if (mvm->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210) {
494*a4128aadSBjoern A. Zeeb 		IWL_DEBUG_RADIO(mvm, "UATS feature is not supported\n");
495*a4128aadSBjoern A. Zeeb 		return;
496*a4128aadSBjoern A. Zeeb 	}
497*a4128aadSBjoern A. Zeeb 
498*a4128aadSBjoern A. Zeeb 	cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd.id,
499*a4128aadSBjoern A. Zeeb 					IWL_FW_CMD_VER_UNKNOWN);
500*a4128aadSBjoern A. Zeeb 	if (cmd_ver != 1) {
501*a4128aadSBjoern A. Zeeb 		IWL_DEBUG_RADIO(mvm,
502*a4128aadSBjoern A. Zeeb 				"MCC_ALLOWED_AP_TYPE_CMD ver %d not supported\n",
503*a4128aadSBjoern A. Zeeb 				cmd_ver);
504*a4128aadSBjoern A. Zeeb 		return;
505*a4128aadSBjoern A. Zeeb 	}
506*a4128aadSBjoern A. Zeeb 
507*a4128aadSBjoern A. Zeeb 	ret = iwl_uefi_get_uats_table(mvm->trans, &mvm->fwrt);
508*a4128aadSBjoern A. Zeeb 	if (ret < 0) {
509*a4128aadSBjoern A. Zeeb 		IWL_DEBUG_FW(mvm, "failed to read UATS table (%d)\n", ret);
510*a4128aadSBjoern A. Zeeb 		return;
511*a4128aadSBjoern A. Zeeb 	}
512*a4128aadSBjoern A. Zeeb 
513*a4128aadSBjoern A. Zeeb 	ret = iwl_mvm_send_cmd(mvm, &cmd);
514*a4128aadSBjoern A. Zeeb 	if (ret < 0)
515*a4128aadSBjoern A. Zeeb 		IWL_ERR(mvm, "failed to send MCC_ALLOWED_AP_TYPE_CMD (%d)\n",
516*a4128aadSBjoern A. Zeeb 			ret);
517*a4128aadSBjoern A. Zeeb 	else
518*a4128aadSBjoern A. Zeeb 		IWL_DEBUG_RADIO(mvm, "MCC_ALLOWED_AP_TYPE_CMD sent to FW\n");
519*a4128aadSBjoern A. Zeeb }
520*a4128aadSBjoern A. Zeeb 
5219af1bba4SBjoern A. Zeeb static int iwl_mvm_sgom_init(struct iwl_mvm *mvm)
5229af1bba4SBjoern A. Zeeb {
5239af1bba4SBjoern A. Zeeb 	u8 cmd_ver;
5249af1bba4SBjoern A. Zeeb 	int ret;
5259af1bba4SBjoern A. Zeeb 	struct iwl_host_cmd cmd = {
5269af1bba4SBjoern A. Zeeb 		.id = WIDE_ID(REGULATORY_AND_NVM_GROUP,
5279af1bba4SBjoern A. Zeeb 			      SAR_OFFSET_MAPPING_TABLE_CMD),
5289af1bba4SBjoern A. Zeeb 		.flags = 0,
5299af1bba4SBjoern A. Zeeb 		.data[0] = &mvm->fwrt.sgom_table,
5309af1bba4SBjoern A. Zeeb 		.len[0] =  sizeof(mvm->fwrt.sgom_table),
5319af1bba4SBjoern A. Zeeb 		.dataflags[0] = IWL_HCMD_DFL_NOCOPY,
5329af1bba4SBjoern A. Zeeb 	};
5339af1bba4SBjoern A. Zeeb 
5349af1bba4SBjoern A. Zeeb 	if (!mvm->fwrt.sgom_enabled) {
5359af1bba4SBjoern A. Zeeb 		IWL_DEBUG_RADIO(mvm, "SGOM table is disabled\n");
5369af1bba4SBjoern A. Zeeb 		return 0;
5379af1bba4SBjoern A. Zeeb 	}
5389af1bba4SBjoern A. Zeeb 
5399af1bba4SBjoern A. Zeeb 	cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd.id,
5409af1bba4SBjoern A. Zeeb 					IWL_FW_CMD_VER_UNKNOWN);
5419af1bba4SBjoern A. Zeeb 
5429af1bba4SBjoern A. Zeeb 	if (cmd_ver != 2) {
5439af1bba4SBjoern A. Zeeb 		IWL_DEBUG_RADIO(mvm, "command version is unsupported. version = %d\n",
5449af1bba4SBjoern A. Zeeb 				cmd_ver);
5459af1bba4SBjoern A. Zeeb 		return 0;
5469af1bba4SBjoern A. Zeeb 	}
5479af1bba4SBjoern A. Zeeb 
5489af1bba4SBjoern A. Zeeb 	ret = iwl_mvm_send_cmd(mvm, &cmd);
5499af1bba4SBjoern A. Zeeb 	if (ret < 0)
5509af1bba4SBjoern A. Zeeb 		IWL_ERR(mvm, "failed to send SAR_OFFSET_MAPPING_CMD (%d)\n", ret);
5519af1bba4SBjoern A. Zeeb 
5529af1bba4SBjoern A. Zeeb 	return ret;
5539af1bba4SBjoern A. Zeeb }
5549af1bba4SBjoern A. Zeeb 
5559af1bba4SBjoern A. Zeeb static int iwl_send_phy_cfg_cmd(struct iwl_mvm *mvm)
5569af1bba4SBjoern A. Zeeb {
5579af1bba4SBjoern A. Zeeb 	u32 cmd_id = PHY_CONFIGURATION_CMD;
5589af1bba4SBjoern A. Zeeb 	struct iwl_phy_cfg_cmd_v3 phy_cfg_cmd;
5599af1bba4SBjoern A. Zeeb 	enum iwl_ucode_type ucode_type = mvm->fwrt.cur_fw_img;
5609af1bba4SBjoern A. Zeeb 	u8 cmd_ver;
5619af1bba4SBjoern A. Zeeb 	size_t cmd_size;
5629af1bba4SBjoern A. Zeeb 
5639af1bba4SBjoern A. Zeeb 	if (iwl_mvm_has_unified_ucode(mvm) &&
5649af1bba4SBjoern A. Zeeb 	    !mvm->trans->cfg->tx_with_siso_diversity)
5659af1bba4SBjoern A. Zeeb 		return 0;
5669af1bba4SBjoern A. Zeeb 
5679af1bba4SBjoern A. Zeeb 	if (mvm->trans->cfg->tx_with_siso_diversity) {
5689af1bba4SBjoern A. Zeeb 		/*
5699af1bba4SBjoern A. Zeeb 		 * TODO: currently we don't set the antenna but letting the NIC
5709af1bba4SBjoern A. Zeeb 		 * to decide which antenna to use. This should come from BIOS.
5719af1bba4SBjoern A. Zeeb 		 */
5729af1bba4SBjoern A. Zeeb 		phy_cfg_cmd.phy_cfg =
5739af1bba4SBjoern A. Zeeb 			cpu_to_le32(FW_PHY_CFG_CHAIN_SAD_ENABLED);
5749af1bba4SBjoern A. Zeeb 	}
5759af1bba4SBjoern A. Zeeb 
5769af1bba4SBjoern A. Zeeb 	/* Set parameters */
5779af1bba4SBjoern A. Zeeb 	phy_cfg_cmd.phy_cfg = cpu_to_le32(iwl_mvm_get_phy_config(mvm));
5789af1bba4SBjoern A. Zeeb 
5799af1bba4SBjoern A. Zeeb 	/* set flags extra PHY configuration flags from the device's cfg */
5809af1bba4SBjoern A. Zeeb 	phy_cfg_cmd.phy_cfg |=
5819af1bba4SBjoern A. Zeeb 		cpu_to_le32(mvm->trans->trans_cfg->extra_phy_cfg_flags);
5829af1bba4SBjoern A. Zeeb 
5839af1bba4SBjoern A. Zeeb 	phy_cfg_cmd.calib_control.event_trigger =
5849af1bba4SBjoern A. Zeeb 		mvm->fw->default_calib[ucode_type].event_trigger;
5859af1bba4SBjoern A. Zeeb 	phy_cfg_cmd.calib_control.flow_trigger =
5869af1bba4SBjoern A. Zeeb 		mvm->fw->default_calib[ucode_type].flow_trigger;
5879af1bba4SBjoern A. Zeeb 
5889af1bba4SBjoern A. Zeeb 	cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id,
5899af1bba4SBjoern A. Zeeb 					IWL_FW_CMD_VER_UNKNOWN);
5909af1bba4SBjoern A. Zeeb 	if (cmd_ver >= 3)
5919af1bba4SBjoern A. Zeeb 		iwl_mvm_phy_filter_init(mvm, &phy_cfg_cmd.phy_specific_cfg);
5929af1bba4SBjoern A. Zeeb 
5939af1bba4SBjoern A. Zeeb 	IWL_DEBUG_INFO(mvm, "Sending Phy CFG command: 0x%x\n",
5949af1bba4SBjoern A. Zeeb 		       phy_cfg_cmd.phy_cfg);
5959af1bba4SBjoern A. Zeeb 	cmd_size = (cmd_ver == 3) ? sizeof(struct iwl_phy_cfg_cmd_v3) :
5969af1bba4SBjoern A. Zeeb 				    sizeof(struct iwl_phy_cfg_cmd_v1);
5979af1bba4SBjoern A. Zeeb 	return iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, cmd_size, &phy_cfg_cmd);
5989af1bba4SBjoern A. Zeeb }
5999af1bba4SBjoern A. Zeeb 
600bfcc09ddSBjoern A. Zeeb static int iwl_run_unified_mvm_ucode(struct iwl_mvm *mvm)
601bfcc09ddSBjoern A. Zeeb {
602bfcc09ddSBjoern A. Zeeb 	struct iwl_notification_wait init_wait;
603bfcc09ddSBjoern A. Zeeb 	struct iwl_nvm_access_complete_cmd nvm_complete = {};
604bfcc09ddSBjoern A. Zeeb 	struct iwl_init_extended_cfg_cmd init_cfg = {
605bfcc09ddSBjoern A. Zeeb 		.init_flags = cpu_to_le32(BIT(IWL_INIT_NVM)),
606bfcc09ddSBjoern A. Zeeb 	};
607bfcc09ddSBjoern A. Zeeb 	static const u16 init_complete[] = {
608bfcc09ddSBjoern A. Zeeb 		INIT_COMPLETE_NOTIF,
609bfcc09ddSBjoern A. Zeeb 	};
610*a4128aadSBjoern A. Zeeb 	u32 sb_cfg;
611bfcc09ddSBjoern A. Zeeb 	int ret;
612bfcc09ddSBjoern A. Zeeb 
613bfcc09ddSBjoern A. Zeeb 	if (mvm->trans->cfg->tx_with_siso_diversity)
614bfcc09ddSBjoern A. Zeeb 		init_cfg.init_flags |= cpu_to_le32(BIT(IWL_INIT_PHY));
615bfcc09ddSBjoern A. Zeeb 
616bfcc09ddSBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
617bfcc09ddSBjoern A. Zeeb 
618bfcc09ddSBjoern A. Zeeb 	mvm->rfkill_safe_init_done = false;
619bfcc09ddSBjoern A. Zeeb 
620*a4128aadSBjoern A. Zeeb 	if (mvm->trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_AX210) {
621*a4128aadSBjoern A. Zeeb 		sb_cfg = iwl_read_umac_prph(mvm->trans, SB_MODIFY_CFG_FLAG);
622*a4128aadSBjoern A. Zeeb 		/* if needed, we'll reset this on our way out later */
623*a4128aadSBjoern A. Zeeb 		mvm->fw_product_reset = sb_cfg == SB_CFG_RESIDES_IN_ROM;
624*a4128aadSBjoern A. Zeeb 		if (mvm->fw_product_reset && iwl_mei_pldr_req())
625*a4128aadSBjoern A. Zeeb 			return -EBUSY;
626*a4128aadSBjoern A. Zeeb 	}
627*a4128aadSBjoern A. Zeeb 
628bfcc09ddSBjoern A. Zeeb 	iwl_init_notification_wait(&mvm->notif_wait,
629bfcc09ddSBjoern A. Zeeb 				   &init_wait,
630bfcc09ddSBjoern A. Zeeb 				   init_complete,
631bfcc09ddSBjoern A. Zeeb 				   ARRAY_SIZE(init_complete),
632bfcc09ddSBjoern A. Zeeb 				   iwl_wait_init_complete,
633bfcc09ddSBjoern A. Zeeb 				   NULL);
634bfcc09ddSBjoern A. Zeeb 
635bfcc09ddSBjoern A. Zeeb 	iwl_dbg_tlv_time_point(&mvm->fwrt, IWL_FW_INI_TIME_POINT_EARLY, NULL);
636bfcc09ddSBjoern A. Zeeb 
637bfcc09ddSBjoern A. Zeeb 	/* Will also start the device */
638bfcc09ddSBjoern A. Zeeb 	ret = iwl_mvm_load_ucode_wait_alive(mvm, IWL_UCODE_REGULAR);
639bfcc09ddSBjoern A. Zeeb 	if (ret) {
640bfcc09ddSBjoern A. Zeeb 		IWL_ERR(mvm, "Failed to start RT ucode: %d\n", ret);
641*a4128aadSBjoern A. Zeeb 
642*a4128aadSBjoern A. Zeeb 		/* if we needed reset then fail here, but notify and remove */
643*a4128aadSBjoern A. Zeeb 		if (mvm->fw_product_reset) {
644*a4128aadSBjoern A. Zeeb 			iwl_mei_alive_notif(false);
645*a4128aadSBjoern A. Zeeb 			iwl_trans_pcie_remove(mvm->trans, true);
646*a4128aadSBjoern A. Zeeb 		}
647*a4128aadSBjoern A. Zeeb 
648bfcc09ddSBjoern A. Zeeb 		goto error;
649bfcc09ddSBjoern A. Zeeb 	}
650bfcc09ddSBjoern A. Zeeb 	iwl_dbg_tlv_time_point(&mvm->fwrt, IWL_FW_INI_TIME_POINT_AFTER_ALIVE,
651bfcc09ddSBjoern A. Zeeb 			       NULL);
652bfcc09ddSBjoern A. Zeeb 
653*a4128aadSBjoern A. Zeeb 	if (mvm->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ)
654*a4128aadSBjoern A. Zeeb 		mvm->trans->step_urm = !!(iwl_read_umac_prph(mvm->trans,
655*a4128aadSBjoern A. Zeeb 							     CNVI_PMU_STEP_FLOW) &
656*a4128aadSBjoern A. Zeeb 						CNVI_PMU_STEP_FLOW_FORCE_URM);
657*a4128aadSBjoern A. Zeeb 
658bfcc09ddSBjoern A. Zeeb 	/* Send init config command to mark that we are sending NVM access
659bfcc09ddSBjoern A. Zeeb 	 * commands
660bfcc09ddSBjoern A. Zeeb 	 */
661bfcc09ddSBjoern A. Zeeb 	ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(SYSTEM_GROUP,
662bfcc09ddSBjoern A. Zeeb 						INIT_EXTENDED_CFG_CMD),
663bfcc09ddSBjoern A. Zeeb 				   CMD_SEND_IN_RFKILL,
664bfcc09ddSBjoern A. Zeeb 				   sizeof(init_cfg), &init_cfg);
665bfcc09ddSBjoern A. Zeeb 	if (ret) {
666bfcc09ddSBjoern A. Zeeb 		IWL_ERR(mvm, "Failed to run init config command: %d\n",
667bfcc09ddSBjoern A. Zeeb 			ret);
668bfcc09ddSBjoern A. Zeeb 		goto error;
669bfcc09ddSBjoern A. Zeeb 	}
670bfcc09ddSBjoern A. Zeeb 
671bfcc09ddSBjoern A. Zeeb 	/* Load NVM to NIC if needed */
672bfcc09ddSBjoern A. Zeeb 	if (mvm->nvm_file_name) {
673bfcc09ddSBjoern A. Zeeb 		ret = iwl_read_external_nvm(mvm->trans, mvm->nvm_file_name,
674bfcc09ddSBjoern A. Zeeb 					    mvm->nvm_sections);
675bfcc09ddSBjoern A. Zeeb 		if (ret)
676bfcc09ddSBjoern A. Zeeb 			goto error;
677bfcc09ddSBjoern A. Zeeb 		ret = iwl_mvm_load_nvm_to_nic(mvm);
678bfcc09ddSBjoern A. Zeeb 		if (ret)
679bfcc09ddSBjoern A. Zeeb 			goto error;
680bfcc09ddSBjoern A. Zeeb 	}
681bfcc09ddSBjoern A. Zeeb 
682bfcc09ddSBjoern A. Zeeb 	ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(REGULATORY_AND_NVM_GROUP,
683bfcc09ddSBjoern A. Zeeb 						NVM_ACCESS_COMPLETE),
684bfcc09ddSBjoern A. Zeeb 				   CMD_SEND_IN_RFKILL,
685bfcc09ddSBjoern A. Zeeb 				   sizeof(nvm_complete), &nvm_complete);
686bfcc09ddSBjoern A. Zeeb 	if (ret) {
687bfcc09ddSBjoern A. Zeeb 		IWL_ERR(mvm, "Failed to run complete NVM access: %d\n",
688bfcc09ddSBjoern A. Zeeb 			ret);
689bfcc09ddSBjoern A. Zeeb 		goto error;
690bfcc09ddSBjoern A. Zeeb 	}
691bfcc09ddSBjoern A. Zeeb 
6929af1bba4SBjoern A. Zeeb 	ret = iwl_send_phy_cfg_cmd(mvm);
6939af1bba4SBjoern A. Zeeb 	if (ret) {
6949af1bba4SBjoern A. Zeeb 		IWL_ERR(mvm, "Failed to run PHY configuration: %d\n",
6959af1bba4SBjoern A. Zeeb 			ret);
6969af1bba4SBjoern A. Zeeb 		goto error;
6979af1bba4SBjoern A. Zeeb 	}
6989af1bba4SBjoern A. Zeeb 
699bfcc09ddSBjoern A. Zeeb 	/* We wait for the INIT complete notification */
700bfcc09ddSBjoern A. Zeeb 	ret = iwl_wait_notification(&mvm->notif_wait, &init_wait,
701bfcc09ddSBjoern A. Zeeb 				    MVM_UCODE_ALIVE_TIMEOUT);
702bfcc09ddSBjoern A. Zeeb 	if (ret)
703bfcc09ddSBjoern A. Zeeb 		return ret;
704bfcc09ddSBjoern A. Zeeb 
705bfcc09ddSBjoern A. Zeeb 	/* Read the NVM only at driver load time, no need to do this twice */
706*a4128aadSBjoern A. Zeeb 	if (!mvm->nvm_data) {
707*a4128aadSBjoern A. Zeeb 		mvm->nvm_data = iwl_get_nvm(mvm->trans, mvm->fw,
708*a4128aadSBjoern A. Zeeb 					    mvm->set_tx_ant, mvm->set_rx_ant);
709bfcc09ddSBjoern A. Zeeb 		if (IS_ERR(mvm->nvm_data)) {
710bfcc09ddSBjoern A. Zeeb 			ret = PTR_ERR(mvm->nvm_data);
711bfcc09ddSBjoern A. Zeeb 			mvm->nvm_data = NULL;
712bfcc09ddSBjoern A. Zeeb 			IWL_ERR(mvm, "Failed to read NVM: %d\n", ret);
713bfcc09ddSBjoern A. Zeeb 			return ret;
714bfcc09ddSBjoern A. Zeeb 		}
715bfcc09ddSBjoern A. Zeeb 	}
716bfcc09ddSBjoern A. Zeeb 
717bfcc09ddSBjoern A. Zeeb 	mvm->rfkill_safe_init_done = true;
718bfcc09ddSBjoern A. Zeeb 
719bfcc09ddSBjoern A. Zeeb 	return 0;
720bfcc09ddSBjoern A. Zeeb 
721bfcc09ddSBjoern A. Zeeb error:
722bfcc09ddSBjoern A. Zeeb 	iwl_remove_notification(&mvm->notif_wait, &init_wait);
723bfcc09ddSBjoern A. Zeeb 	return ret;
724bfcc09ddSBjoern A. Zeeb }
725bfcc09ddSBjoern A. Zeeb 
726bfcc09ddSBjoern A. Zeeb int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm)
727bfcc09ddSBjoern A. Zeeb {
728bfcc09ddSBjoern A. Zeeb 	struct iwl_notification_wait calib_wait;
729bfcc09ddSBjoern A. Zeeb 	static const u16 init_complete[] = {
730bfcc09ddSBjoern A. Zeeb 		INIT_COMPLETE_NOTIF,
731bfcc09ddSBjoern A. Zeeb 		CALIB_RES_NOTIF_PHY_DB
732bfcc09ddSBjoern A. Zeeb 	};
733bfcc09ddSBjoern A. Zeeb 	int ret;
734bfcc09ddSBjoern A. Zeeb 
735bfcc09ddSBjoern A. Zeeb 	if (iwl_mvm_has_unified_ucode(mvm))
736bfcc09ddSBjoern A. Zeeb 		return iwl_run_unified_mvm_ucode(mvm);
737bfcc09ddSBjoern A. Zeeb 
738bfcc09ddSBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
739bfcc09ddSBjoern A. Zeeb 
740bfcc09ddSBjoern A. Zeeb 	mvm->rfkill_safe_init_done = false;
741bfcc09ddSBjoern A. Zeeb 
742bfcc09ddSBjoern A. Zeeb 	iwl_init_notification_wait(&mvm->notif_wait,
743bfcc09ddSBjoern A. Zeeb 				   &calib_wait,
744bfcc09ddSBjoern A. Zeeb 				   init_complete,
745bfcc09ddSBjoern A. Zeeb 				   ARRAY_SIZE(init_complete),
746bfcc09ddSBjoern A. Zeeb 				   iwl_wait_phy_db_entry,
747bfcc09ddSBjoern A. Zeeb 				   mvm->phy_db);
748bfcc09ddSBjoern A. Zeeb 
749bfcc09ddSBjoern A. Zeeb 	iwl_dbg_tlv_time_point(&mvm->fwrt, IWL_FW_INI_TIME_POINT_EARLY, NULL);
750bfcc09ddSBjoern A. Zeeb 
751bfcc09ddSBjoern A. Zeeb 	/* Will also start the device */
752bfcc09ddSBjoern A. Zeeb 	ret = iwl_mvm_load_ucode_wait_alive(mvm, IWL_UCODE_INIT);
753bfcc09ddSBjoern A. Zeeb 	if (ret) {
754bfcc09ddSBjoern A. Zeeb 		IWL_ERR(mvm, "Failed to start INIT ucode: %d\n", ret);
755bfcc09ddSBjoern A. Zeeb 		goto remove_notif;
756bfcc09ddSBjoern A. Zeeb 	}
757bfcc09ddSBjoern A. Zeeb 
758bfcc09ddSBjoern A. Zeeb 	if (mvm->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_8000) {
759bfcc09ddSBjoern A. Zeeb 		ret = iwl_mvm_send_bt_init_conf(mvm);
760bfcc09ddSBjoern A. Zeeb 		if (ret)
761bfcc09ddSBjoern A. Zeeb 			goto remove_notif;
762bfcc09ddSBjoern A. Zeeb 	}
763bfcc09ddSBjoern A. Zeeb 
764bfcc09ddSBjoern A. Zeeb 	/* Read the NVM only at driver load time, no need to do this twice */
765bfcc09ddSBjoern A. Zeeb 	if (!mvm->nvm_data) {
766bfcc09ddSBjoern A. Zeeb 		ret = iwl_nvm_init(mvm);
767bfcc09ddSBjoern A. Zeeb 		if (ret) {
768bfcc09ddSBjoern A. Zeeb 			IWL_ERR(mvm, "Failed to read NVM: %d\n", ret);
769bfcc09ddSBjoern A. Zeeb 			goto remove_notif;
770bfcc09ddSBjoern A. Zeeb 		}
771bfcc09ddSBjoern A. Zeeb 	}
772bfcc09ddSBjoern A. Zeeb 
773bfcc09ddSBjoern A. Zeeb 	/* In case we read the NVM from external file, load it to the NIC */
774bfcc09ddSBjoern A. Zeeb 	if (mvm->nvm_file_name) {
775bfcc09ddSBjoern A. Zeeb 		ret = iwl_mvm_load_nvm_to_nic(mvm);
776bfcc09ddSBjoern A. Zeeb 		if (ret)
777bfcc09ddSBjoern A. Zeeb 			goto remove_notif;
778bfcc09ddSBjoern A. Zeeb 	}
779bfcc09ddSBjoern A. Zeeb 
780bfcc09ddSBjoern A. Zeeb 	WARN_ONCE(mvm->nvm_data->nvm_version < mvm->trans->cfg->nvm_ver,
781bfcc09ddSBjoern A. Zeeb 		  "Too old NVM version (0x%0x, required = 0x%0x)",
782bfcc09ddSBjoern A. Zeeb 		  mvm->nvm_data->nvm_version, mvm->trans->cfg->nvm_ver);
783bfcc09ddSBjoern A. Zeeb 
784bfcc09ddSBjoern A. Zeeb 	/*
785bfcc09ddSBjoern A. Zeeb 	 * abort after reading the nvm in case RF Kill is on, we will complete
786bfcc09ddSBjoern A. Zeeb 	 * the init seq later when RF kill will switch to off
787bfcc09ddSBjoern A. Zeeb 	 */
788bfcc09ddSBjoern A. Zeeb 	if (iwl_mvm_is_radio_hw_killed(mvm)) {
789bfcc09ddSBjoern A. Zeeb 		IWL_DEBUG_RF_KILL(mvm,
790bfcc09ddSBjoern A. Zeeb 				  "jump over all phy activities due to RF kill\n");
791bfcc09ddSBjoern A. Zeeb 		goto remove_notif;
792bfcc09ddSBjoern A. Zeeb 	}
793bfcc09ddSBjoern A. Zeeb 
794bfcc09ddSBjoern A. Zeeb 	mvm->rfkill_safe_init_done = true;
795bfcc09ddSBjoern A. Zeeb 
796bfcc09ddSBjoern A. Zeeb 	/* Send TX valid antennas before triggering calibrations */
797bfcc09ddSBjoern A. Zeeb 	ret = iwl_send_tx_ant_cfg(mvm, iwl_mvm_get_valid_tx_ant(mvm));
798bfcc09ddSBjoern A. Zeeb 	if (ret)
799bfcc09ddSBjoern A. Zeeb 		goto remove_notif;
800bfcc09ddSBjoern A. Zeeb 
801bfcc09ddSBjoern A. Zeeb 	ret = iwl_send_phy_cfg_cmd(mvm);
802bfcc09ddSBjoern A. Zeeb 	if (ret) {
803bfcc09ddSBjoern A. Zeeb 		IWL_ERR(mvm, "Failed to run INIT calibrations: %d\n",
804bfcc09ddSBjoern A. Zeeb 			ret);
805bfcc09ddSBjoern A. Zeeb 		goto remove_notif;
806bfcc09ddSBjoern A. Zeeb 	}
807bfcc09ddSBjoern A. Zeeb 
808bfcc09ddSBjoern A. Zeeb 	/*
809bfcc09ddSBjoern A. Zeeb 	 * Some things may run in the background now, but we
810bfcc09ddSBjoern A. Zeeb 	 * just wait for the calibration complete notification.
811bfcc09ddSBjoern A. Zeeb 	 */
812bfcc09ddSBjoern A. Zeeb 	ret = iwl_wait_notification(&mvm->notif_wait, &calib_wait,
813bfcc09ddSBjoern A. Zeeb 				    MVM_UCODE_CALIB_TIMEOUT);
814bfcc09ddSBjoern A. Zeeb 	if (!ret)
815bfcc09ddSBjoern A. Zeeb 		goto out;
816bfcc09ddSBjoern A. Zeeb 
817bfcc09ddSBjoern A. Zeeb 	if (iwl_mvm_is_radio_hw_killed(mvm)) {
818bfcc09ddSBjoern A. Zeeb 		IWL_DEBUG_RF_KILL(mvm, "RFKILL while calibrating.\n");
819bfcc09ddSBjoern A. Zeeb 		ret = 0;
820bfcc09ddSBjoern A. Zeeb 	} else {
821bfcc09ddSBjoern A. Zeeb 		IWL_ERR(mvm, "Failed to run INIT calibrations: %d\n",
822bfcc09ddSBjoern A. Zeeb 			ret);
823bfcc09ddSBjoern A. Zeeb 	}
824bfcc09ddSBjoern A. Zeeb 
825bfcc09ddSBjoern A. Zeeb 	goto out;
826bfcc09ddSBjoern A. Zeeb 
827bfcc09ddSBjoern A. Zeeb remove_notif:
828bfcc09ddSBjoern A. Zeeb 	iwl_remove_notification(&mvm->notif_wait, &calib_wait);
829bfcc09ddSBjoern A. Zeeb out:
830bfcc09ddSBjoern A. Zeeb 	mvm->rfkill_safe_init_done = false;
831*a4128aadSBjoern A. Zeeb 	if (!mvm->nvm_data) {
832bfcc09ddSBjoern A. Zeeb 		/* we want to debug INIT and we have no NVM - fake */
833bfcc09ddSBjoern A. Zeeb 		mvm->nvm_data = kzalloc(sizeof(struct iwl_nvm_data) +
834bfcc09ddSBjoern A. Zeeb 					sizeof(struct ieee80211_channel) +
835bfcc09ddSBjoern A. Zeeb 					sizeof(struct ieee80211_rate),
836bfcc09ddSBjoern A. Zeeb 					GFP_KERNEL);
837bfcc09ddSBjoern A. Zeeb 		if (!mvm->nvm_data)
838bfcc09ddSBjoern A. Zeeb 			return -ENOMEM;
839bfcc09ddSBjoern A. Zeeb 		mvm->nvm_data->bands[0].channels = mvm->nvm_data->channels;
840bfcc09ddSBjoern A. Zeeb 		mvm->nvm_data->bands[0].n_channels = 1;
841bfcc09ddSBjoern A. Zeeb 		mvm->nvm_data->bands[0].n_bitrates = 1;
842bfcc09ddSBjoern A. Zeeb 		mvm->nvm_data->bands[0].bitrates =
843bfcc09ddSBjoern A. Zeeb 			(void *)(mvm->nvm_data->channels + 1);
844bfcc09ddSBjoern A. Zeeb 		mvm->nvm_data->bands[0].bitrates->hw_value = 10;
845bfcc09ddSBjoern A. Zeeb 	}
846bfcc09ddSBjoern A. Zeeb 
847bfcc09ddSBjoern A. Zeeb 	return ret;
848bfcc09ddSBjoern A. Zeeb }
849bfcc09ddSBjoern A. Zeeb 
850bfcc09ddSBjoern A. Zeeb static int iwl_mvm_config_ltr(struct iwl_mvm *mvm)
851bfcc09ddSBjoern A. Zeeb {
852bfcc09ddSBjoern A. Zeeb 	struct iwl_ltr_config_cmd cmd = {
853bfcc09ddSBjoern A. Zeeb 		.flags = cpu_to_le32(LTR_CFG_FLAG_FEATURE_ENABLE),
854bfcc09ddSBjoern A. Zeeb 	};
855bfcc09ddSBjoern A. Zeeb 
856bfcc09ddSBjoern A. Zeeb 	if (!mvm->trans->ltr_enabled)
857bfcc09ddSBjoern A. Zeeb 		return 0;
858bfcc09ddSBjoern A. Zeeb 
859bfcc09ddSBjoern A. Zeeb 	return iwl_mvm_send_cmd_pdu(mvm, LTR_CONFIG, 0,
860bfcc09ddSBjoern A. Zeeb 				    sizeof(cmd), &cmd);
861bfcc09ddSBjoern A. Zeeb }
862bfcc09ddSBjoern A. Zeeb 
863bfcc09ddSBjoern A. Zeeb int iwl_mvm_sar_select_profile(struct iwl_mvm *mvm, int prof_a, int prof_b)
864bfcc09ddSBjoern A. Zeeb {
865d9836fb4SBjoern A. Zeeb 	u32 cmd_id = REDUCE_TX_POWER_CMD;
866bfcc09ddSBjoern A. Zeeb 	struct iwl_dev_tx_power_cmd cmd = {
867bfcc09ddSBjoern A. Zeeb 		.common.set_mode = cpu_to_le32(IWL_TX_POWER_MODE_SET_CHAINS),
868bfcc09ddSBjoern A. Zeeb 	};
869bfcc09ddSBjoern A. Zeeb 	__le16 *per_chain;
870bfcc09ddSBjoern A. Zeeb 	int ret;
871bfcc09ddSBjoern A. Zeeb 	u16 len = 0;
872bfcc09ddSBjoern A. Zeeb 	u32 n_subbands;
873*a4128aadSBjoern A. Zeeb 	u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, 3);
874*a4128aadSBjoern A. Zeeb 
875*a4128aadSBjoern A. Zeeb 	if (cmd_ver >= 7) {
876fac1f593SBjoern A. Zeeb 		len = sizeof(cmd.v7);
877fac1f593SBjoern A. Zeeb 		n_subbands = IWL_NUM_SUB_BANDS_V2;
878fac1f593SBjoern A. Zeeb 		per_chain = cmd.v7.per_chain[0][0];
879fac1f593SBjoern A. Zeeb 		cmd.v7.flags = cpu_to_le32(mvm->fwrt.reduced_power_flags);
880*a4128aadSBjoern A. Zeeb 		if (cmd_ver == 8)
881*a4128aadSBjoern A. Zeeb 			len = sizeof(cmd.v8);
882fac1f593SBjoern A. Zeeb 	} else if (cmd_ver == 6) {
883bfcc09ddSBjoern A. Zeeb 		len = sizeof(cmd.v6);
884bfcc09ddSBjoern A. Zeeb 		n_subbands = IWL_NUM_SUB_BANDS_V2;
885bfcc09ddSBjoern A. Zeeb 		per_chain = cmd.v6.per_chain[0][0];
886bfcc09ddSBjoern A. Zeeb 	} else if (fw_has_api(&mvm->fw->ucode_capa,
887bfcc09ddSBjoern A. Zeeb 			      IWL_UCODE_TLV_API_REDUCE_TX_POWER)) {
888bfcc09ddSBjoern A. Zeeb 		len = sizeof(cmd.v5);
889bfcc09ddSBjoern A. Zeeb 		n_subbands = IWL_NUM_SUB_BANDS_V1;
890bfcc09ddSBjoern A. Zeeb 		per_chain = cmd.v5.per_chain[0][0];
891bfcc09ddSBjoern A. Zeeb 	} else if (fw_has_capa(&mvm->fw->ucode_capa,
892bfcc09ddSBjoern A. Zeeb 			       IWL_UCODE_TLV_CAPA_TX_POWER_ACK)) {
893bfcc09ddSBjoern A. Zeeb 		len = sizeof(cmd.v4);
894bfcc09ddSBjoern A. Zeeb 		n_subbands = IWL_NUM_SUB_BANDS_V1;
895bfcc09ddSBjoern A. Zeeb 		per_chain = cmd.v4.per_chain[0][0];
896bfcc09ddSBjoern A. Zeeb 	} else {
897bfcc09ddSBjoern A. Zeeb 		len = sizeof(cmd.v3);
898bfcc09ddSBjoern A. Zeeb 		n_subbands = IWL_NUM_SUB_BANDS_V1;
899bfcc09ddSBjoern A. Zeeb 		per_chain = cmd.v3.per_chain[0][0];
900bfcc09ddSBjoern A. Zeeb 	}
901bfcc09ddSBjoern A. Zeeb 
902bfcc09ddSBjoern A. Zeeb 	/* all structs have the same common part, add it */
903bfcc09ddSBjoern A. Zeeb 	len += sizeof(cmd.common);
904bfcc09ddSBjoern A. Zeeb 
905*a4128aadSBjoern A. Zeeb 	ret = iwl_sar_fill_profile(&mvm->fwrt, per_chain,
906bfcc09ddSBjoern A. Zeeb 				   IWL_NUM_CHAIN_TABLES,
907bfcc09ddSBjoern A. Zeeb 				   n_subbands, prof_a, prof_b);
908bfcc09ddSBjoern A. Zeeb 
909bfcc09ddSBjoern A. Zeeb 	/* return on error or if the profile is disabled (positive number) */
910bfcc09ddSBjoern A. Zeeb 	if (ret)
911bfcc09ddSBjoern A. Zeeb 		return ret;
912bfcc09ddSBjoern A. Zeeb 
913d9836fb4SBjoern A. Zeeb 	iwl_mei_set_power_limit(per_chain);
914d9836fb4SBjoern A. Zeeb 
915bfcc09ddSBjoern A. Zeeb 	IWL_DEBUG_RADIO(mvm, "Sending REDUCE_TX_POWER_CMD per chain\n");
916d9836fb4SBjoern A. Zeeb 	return iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, len, &cmd);
917bfcc09ddSBjoern A. Zeeb }
918bfcc09ddSBjoern A. Zeeb 
919bfcc09ddSBjoern A. Zeeb int iwl_mvm_get_sar_geo_profile(struct iwl_mvm *mvm)
920bfcc09ddSBjoern A. Zeeb {
921bfcc09ddSBjoern A. Zeeb 	union iwl_geo_tx_power_profiles_cmd geo_tx_cmd;
922bfcc09ddSBjoern A. Zeeb 	struct iwl_geo_tx_power_profiles_resp *resp;
923bfcc09ddSBjoern A. Zeeb 	u16 len;
924bfcc09ddSBjoern A. Zeeb 	int ret;
925d9836fb4SBjoern A. Zeeb 	struct iwl_host_cmd cmd = {
926d9836fb4SBjoern A. Zeeb 		.id = WIDE_ID(PHY_OPS_GROUP, PER_CHAIN_LIMIT_OFFSET_CMD),
927d9836fb4SBjoern A. Zeeb 		.flags = CMD_WANT_SKB,
928d9836fb4SBjoern A. Zeeb 		.data = { &geo_tx_cmd },
929d9836fb4SBjoern A. Zeeb 	};
930d9836fb4SBjoern A. Zeeb 	u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd.id,
931bfcc09ddSBjoern A. Zeeb 					   IWL_FW_CMD_VER_UNKNOWN);
932bfcc09ddSBjoern A. Zeeb 
933bfcc09ddSBjoern A. Zeeb 	/* the ops field is at the same spot for all versions, so set in v1 */
934bfcc09ddSBjoern A. Zeeb 	geo_tx_cmd.v1.ops =
935bfcc09ddSBjoern A. Zeeb 		cpu_to_le32(IWL_PER_CHAIN_OFFSET_GET_CURRENT_TABLE);
936bfcc09ddSBjoern A. Zeeb 
937bfcc09ddSBjoern A. Zeeb 	if (cmd_ver == 5)
938bfcc09ddSBjoern A. Zeeb 		len = sizeof(geo_tx_cmd.v5);
939bfcc09ddSBjoern A. Zeeb 	else if (cmd_ver == 4)
940bfcc09ddSBjoern A. Zeeb 		len = sizeof(geo_tx_cmd.v4);
941bfcc09ddSBjoern A. Zeeb 	else if (cmd_ver == 3)
942bfcc09ddSBjoern A. Zeeb 		len = sizeof(geo_tx_cmd.v3);
943bfcc09ddSBjoern A. Zeeb 	else if (fw_has_api(&mvm->fwrt.fw->ucode_capa,
944bfcc09ddSBjoern A. Zeeb 			    IWL_UCODE_TLV_API_SAR_TABLE_VER))
945bfcc09ddSBjoern A. Zeeb 		len = sizeof(geo_tx_cmd.v2);
946bfcc09ddSBjoern A. Zeeb 	else
947bfcc09ddSBjoern A. Zeeb 		len = sizeof(geo_tx_cmd.v1);
948bfcc09ddSBjoern A. Zeeb 
949bfcc09ddSBjoern A. Zeeb 	if (!iwl_sar_geo_support(&mvm->fwrt))
950bfcc09ddSBjoern A. Zeeb 		return -EOPNOTSUPP;
951bfcc09ddSBjoern A. Zeeb 
952d9836fb4SBjoern A. Zeeb 	cmd.len[0] = len;
953bfcc09ddSBjoern A. Zeeb 
954bfcc09ddSBjoern A. Zeeb 	ret = iwl_mvm_send_cmd(mvm, &cmd);
955bfcc09ddSBjoern A. Zeeb 	if (ret) {
956bfcc09ddSBjoern A. Zeeb 		IWL_ERR(mvm, "Failed to get geographic profile info %d\n", ret);
957bfcc09ddSBjoern A. Zeeb 		return ret;
958bfcc09ddSBjoern A. Zeeb 	}
959bfcc09ddSBjoern A. Zeeb 
960bfcc09ddSBjoern A. Zeeb 	resp = (void *)cmd.resp_pkt->data;
961bfcc09ddSBjoern A. Zeeb 	ret = le32_to_cpu(resp->profile_idx);
962bfcc09ddSBjoern A. Zeeb 
963*a4128aadSBjoern A. Zeeb 	if (WARN_ON(ret > BIOS_GEO_MAX_PROFILE_NUM))
964bfcc09ddSBjoern A. Zeeb 		ret = -EIO;
965bfcc09ddSBjoern A. Zeeb 
966bfcc09ddSBjoern A. Zeeb 	iwl_free_resp(&cmd);
967bfcc09ddSBjoern A. Zeeb 	return ret;
968bfcc09ddSBjoern A. Zeeb }
969bfcc09ddSBjoern A. Zeeb 
970bfcc09ddSBjoern A. Zeeb static int iwl_mvm_sar_geo_init(struct iwl_mvm *mvm)
971bfcc09ddSBjoern A. Zeeb {
972d9836fb4SBjoern A. Zeeb 	u32 cmd_id = WIDE_ID(PHY_OPS_GROUP, PER_CHAIN_LIMIT_OFFSET_CMD);
973bfcc09ddSBjoern A. Zeeb 	union iwl_geo_tx_power_profiles_cmd cmd;
974bfcc09ddSBjoern A. Zeeb 	u16 len;
975bfcc09ddSBjoern A. Zeeb 	u32 n_bands;
976bfcc09ddSBjoern A. Zeeb 	u32 n_profiles;
977*a4128aadSBjoern A. Zeeb 	__le32 sk = cpu_to_le32(0);
978bfcc09ddSBjoern A. Zeeb 	int ret;
979d9836fb4SBjoern A. Zeeb 	u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id,
980bfcc09ddSBjoern A. Zeeb 					   IWL_FW_CMD_VER_UNKNOWN);
981bfcc09ddSBjoern A. Zeeb 
982bfcc09ddSBjoern A. Zeeb 	BUILD_BUG_ON(offsetof(struct iwl_geo_tx_power_profiles_cmd_v1, ops) !=
983bfcc09ddSBjoern A. Zeeb 		     offsetof(struct iwl_geo_tx_power_profiles_cmd_v2, ops) ||
984bfcc09ddSBjoern A. Zeeb 		     offsetof(struct iwl_geo_tx_power_profiles_cmd_v2, ops) !=
985bfcc09ddSBjoern A. Zeeb 		     offsetof(struct iwl_geo_tx_power_profiles_cmd_v3, ops) ||
986bfcc09ddSBjoern A. Zeeb 		     offsetof(struct iwl_geo_tx_power_profiles_cmd_v3, ops) !=
987bfcc09ddSBjoern A. Zeeb 		     offsetof(struct iwl_geo_tx_power_profiles_cmd_v4, ops) ||
988bfcc09ddSBjoern A. Zeeb 		     offsetof(struct iwl_geo_tx_power_profiles_cmd_v4, ops) !=
989bfcc09ddSBjoern A. Zeeb 		     offsetof(struct iwl_geo_tx_power_profiles_cmd_v5, ops));
990bfcc09ddSBjoern A. Zeeb 
991bfcc09ddSBjoern A. Zeeb 	/* the ops field is at the same spot for all versions, so set in v1 */
992bfcc09ddSBjoern A. Zeeb 	cmd.v1.ops = cpu_to_le32(IWL_PER_CHAIN_OFFSET_SET_TABLES);
993bfcc09ddSBjoern A. Zeeb 
994*a4128aadSBjoern A. Zeeb 	/* Only set to South Korea if the table revision is 1 */
995*a4128aadSBjoern A. Zeeb 	if (mvm->fwrt.geo_rev == 1)
996*a4128aadSBjoern A. Zeeb 		sk = cpu_to_le32(1);
997*a4128aadSBjoern A. Zeeb 
998bfcc09ddSBjoern A. Zeeb 	if (cmd_ver == 5) {
999bfcc09ddSBjoern A. Zeeb 		len = sizeof(cmd.v5);
1000bfcc09ddSBjoern A. Zeeb 		n_bands = ARRAY_SIZE(cmd.v5.table[0]);
1001*a4128aadSBjoern A. Zeeb 		n_profiles = BIOS_GEO_MAX_PROFILE_NUM;
1002*a4128aadSBjoern A. Zeeb 		cmd.v5.table_revision = sk;
1003bfcc09ddSBjoern A. Zeeb 	} else if (cmd_ver == 4) {
1004bfcc09ddSBjoern A. Zeeb 		len = sizeof(cmd.v4);
1005bfcc09ddSBjoern A. Zeeb 		n_bands = ARRAY_SIZE(cmd.v4.table[0]);
1006*a4128aadSBjoern A. Zeeb 		n_profiles = BIOS_GEO_MAX_PROFILE_NUM;
1007*a4128aadSBjoern A. Zeeb 		cmd.v4.table_revision = sk;
1008bfcc09ddSBjoern A. Zeeb 	} else if (cmd_ver == 3) {
1009bfcc09ddSBjoern A. Zeeb 		len = sizeof(cmd.v3);
1010bfcc09ddSBjoern A. Zeeb 		n_bands = ARRAY_SIZE(cmd.v3.table[0]);
1011*a4128aadSBjoern A. Zeeb 		n_profiles = BIOS_GEO_MIN_PROFILE_NUM;
1012*a4128aadSBjoern A. Zeeb 		cmd.v3.table_revision = sk;
1013bfcc09ddSBjoern A. Zeeb 	} else if (fw_has_api(&mvm->fwrt.fw->ucode_capa,
1014bfcc09ddSBjoern A. Zeeb 			      IWL_UCODE_TLV_API_SAR_TABLE_VER)) {
1015bfcc09ddSBjoern A. Zeeb 		len = sizeof(cmd.v2);
1016bfcc09ddSBjoern A. Zeeb 		n_bands = ARRAY_SIZE(cmd.v2.table[0]);
1017*a4128aadSBjoern A. Zeeb 		n_profiles = BIOS_GEO_MIN_PROFILE_NUM;
1018*a4128aadSBjoern A. Zeeb 		cmd.v2.table_revision = sk;
1019bfcc09ddSBjoern A. Zeeb 	} else {
1020bfcc09ddSBjoern A. Zeeb 		len = sizeof(cmd.v1);
1021bfcc09ddSBjoern A. Zeeb 		n_bands = ARRAY_SIZE(cmd.v1.table[0]);
1022*a4128aadSBjoern A. Zeeb 		n_profiles = BIOS_GEO_MIN_PROFILE_NUM;
1023bfcc09ddSBjoern A. Zeeb 	}
1024bfcc09ddSBjoern A. Zeeb 
1025bfcc09ddSBjoern A. Zeeb 	BUILD_BUG_ON(offsetof(struct iwl_geo_tx_power_profiles_cmd_v1, table) !=
1026bfcc09ddSBjoern A. Zeeb 		     offsetof(struct iwl_geo_tx_power_profiles_cmd_v2, table) ||
1027bfcc09ddSBjoern A. Zeeb 		     offsetof(struct iwl_geo_tx_power_profiles_cmd_v2, table) !=
1028bfcc09ddSBjoern A. Zeeb 		     offsetof(struct iwl_geo_tx_power_profiles_cmd_v3, table) ||
1029bfcc09ddSBjoern A. Zeeb 		     offsetof(struct iwl_geo_tx_power_profiles_cmd_v3, table) !=
1030bfcc09ddSBjoern A. Zeeb 		     offsetof(struct iwl_geo_tx_power_profiles_cmd_v4, table) ||
1031bfcc09ddSBjoern A. Zeeb 		     offsetof(struct iwl_geo_tx_power_profiles_cmd_v4, table) !=
1032bfcc09ddSBjoern A. Zeeb 		     offsetof(struct iwl_geo_tx_power_profiles_cmd_v5, table));
1033bfcc09ddSBjoern A. Zeeb 	/* the table is at the same position for all versions, so set use v1 */
1034*a4128aadSBjoern A. Zeeb 	ret = iwl_sar_geo_fill_table(&mvm->fwrt, &cmd.v1.table[0][0],
1035bfcc09ddSBjoern A. Zeeb 				     n_bands, n_profiles);
1036bfcc09ddSBjoern A. Zeeb 
1037bfcc09ddSBjoern A. Zeeb 	/*
1038bfcc09ddSBjoern A. Zeeb 	 * It is a valid scenario to not support SAR, or miss wgds table,
1039bfcc09ddSBjoern A. Zeeb 	 * but in that case there is no need to send the command.
1040bfcc09ddSBjoern A. Zeeb 	 */
1041bfcc09ddSBjoern A. Zeeb 	if (ret)
1042bfcc09ddSBjoern A. Zeeb 		return 0;
1043bfcc09ddSBjoern A. Zeeb 
1044d9836fb4SBjoern A. Zeeb 	return iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, len, &cmd);
1045bfcc09ddSBjoern A. Zeeb }
1046bfcc09ddSBjoern A. Zeeb 
1047bfcc09ddSBjoern A. Zeeb int iwl_mvm_ppag_send_cmd(struct iwl_mvm *mvm)
1048bfcc09ddSBjoern A. Zeeb {
1049d9836fb4SBjoern A. Zeeb 	union iwl_ppag_table_cmd cmd;
1050fac1f593SBjoern A. Zeeb 	int ret, cmd_size;
1051bfcc09ddSBjoern A. Zeeb 
1052*a4128aadSBjoern A. Zeeb 	ret = iwl_fill_ppag_table(&mvm->fwrt, &cmd, &cmd_size);
105388a15f72SBjoern A. Zeeb 	/* Not supporting PPAG table is a valid scenario */
1054fac1f593SBjoern A. Zeeb 	if (ret < 0)
105588a15f72SBjoern A. Zeeb 		return 0;
1056bfcc09ddSBjoern A. Zeeb 
1057bfcc09ddSBjoern A. Zeeb 	IWL_DEBUG_RADIO(mvm, "Sending PER_PLATFORM_ANT_GAIN_CMD\n");
1058bfcc09ddSBjoern A. Zeeb 	ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(PHY_OPS_GROUP,
1059bfcc09ddSBjoern A. Zeeb 						PER_PLATFORM_ANT_GAIN_CMD),
1060d9836fb4SBjoern A. Zeeb 				   0, cmd_size, &cmd);
1061bfcc09ddSBjoern A. Zeeb 	if (ret < 0)
1062bfcc09ddSBjoern A. Zeeb 		IWL_ERR(mvm, "failed to send PER_PLATFORM_ANT_GAIN_CMD (%d)\n",
1063bfcc09ddSBjoern A. Zeeb 			ret);
1064bfcc09ddSBjoern A. Zeeb 
1065bfcc09ddSBjoern A. Zeeb 	return ret;
1066bfcc09ddSBjoern A. Zeeb }
1067bfcc09ddSBjoern A. Zeeb 
1068bfcc09ddSBjoern A. Zeeb static int iwl_mvm_ppag_init(struct iwl_mvm *mvm)
1069bfcc09ddSBjoern A. Zeeb {
1070bfcc09ddSBjoern A. Zeeb 	/* no need to read the table, done in INIT stage */
1071*a4128aadSBjoern A. Zeeb 	if (!(iwl_is_ppag_approved(&mvm->fwrt)))
1072bfcc09ddSBjoern A. Zeeb 		return 0;
1073bfcc09ddSBjoern A. Zeeb 
1074bfcc09ddSBjoern A. Zeeb 	return iwl_mvm_ppag_send_cmd(mvm);
1075bfcc09ddSBjoern A. Zeeb }
1076bfcc09ddSBjoern A. Zeeb 
1077d9836fb4SBjoern A. Zeeb static bool iwl_mvm_add_to_tas_block_list(__le32 *list, __le32 *le_size, unsigned int mcc)
1078d9836fb4SBjoern A. Zeeb {
1079d9836fb4SBjoern A. Zeeb 	int i;
1080d9836fb4SBjoern A. Zeeb 	u32 size = le32_to_cpu(*le_size);
1081d9836fb4SBjoern A. Zeeb 
1082d9836fb4SBjoern A. Zeeb 	/* Verify that there is room for another country */
1083*a4128aadSBjoern A. Zeeb 	if (size >= IWL_WTAS_BLACK_LIST_MAX)
1084d9836fb4SBjoern A. Zeeb 		return false;
1085d9836fb4SBjoern A. Zeeb 
1086d9836fb4SBjoern A. Zeeb 	for (i = 0; i < size; i++) {
1087d9836fb4SBjoern A. Zeeb 		if (list[i] == cpu_to_le32(mcc))
1088d9836fb4SBjoern A. Zeeb 			return true;
1089d9836fb4SBjoern A. Zeeb 	}
1090d9836fb4SBjoern A. Zeeb 
1091d9836fb4SBjoern A. Zeeb 	list[size++] = cpu_to_le32(mcc);
1092d9836fb4SBjoern A. Zeeb 	*le_size = cpu_to_le32(size);
1093d9836fb4SBjoern A. Zeeb 	return true;
1094d9836fb4SBjoern A. Zeeb }
1095d9836fb4SBjoern A. Zeeb 
1096bfcc09ddSBjoern A. Zeeb static void iwl_mvm_tas_init(struct iwl_mvm *mvm)
1097bfcc09ddSBjoern A. Zeeb {
1098d9836fb4SBjoern A. Zeeb 	u32 cmd_id = WIDE_ID(REGULATORY_AND_NVM_GROUP, TAS_CONFIG);
1099bfcc09ddSBjoern A. Zeeb 	int ret;
1100*a4128aadSBjoern A. Zeeb 	struct iwl_tas_data data = {};
1101*a4128aadSBjoern A. Zeeb 	struct iwl_tas_config_cmd cmd = {};
1102d9836fb4SBjoern A. Zeeb 	int cmd_size, fw_ver;
1103bfcc09ddSBjoern A. Zeeb 
1104*a4128aadSBjoern A. Zeeb 	BUILD_BUG_ON(ARRAY_SIZE(data.block_list_array) !=
1105*a4128aadSBjoern A. Zeeb 		     IWL_WTAS_BLACK_LIST_MAX);
1106*a4128aadSBjoern A. Zeeb 	BUILD_BUG_ON(ARRAY_SIZE(cmd.common.block_list_array) !=
1107*a4128aadSBjoern A. Zeeb 		     IWL_WTAS_BLACK_LIST_MAX);
1108bfcc09ddSBjoern A. Zeeb 
1109bfcc09ddSBjoern A. Zeeb 	if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TAS_CFG)) {
1110bfcc09ddSBjoern A. Zeeb 		IWL_DEBUG_RADIO(mvm, "TAS not enabled in FW\n");
1111bfcc09ddSBjoern A. Zeeb 		return;
1112bfcc09ddSBjoern A. Zeeb 	}
1113bfcc09ddSBjoern A. Zeeb 
1114*a4128aadSBjoern A. Zeeb 	ret = iwl_bios_get_tas_table(&mvm->fwrt, &data);
1115bfcc09ddSBjoern A. Zeeb 	if (ret < 0) {
1116bfcc09ddSBjoern A. Zeeb 		IWL_DEBUG_RADIO(mvm,
1117bfcc09ddSBjoern A. Zeeb 				"TAS table invalid or unavailable. (%d)\n",
1118bfcc09ddSBjoern A. Zeeb 				ret);
1119bfcc09ddSBjoern A. Zeeb 		return;
1120bfcc09ddSBjoern A. Zeeb 	}
1121bfcc09ddSBjoern A. Zeeb 
1122d9836fb4SBjoern A. Zeeb 	if (ret == 0)
1123bfcc09ddSBjoern A. Zeeb 		return;
1124bfcc09ddSBjoern A. Zeeb 
1125*a4128aadSBjoern A. Zeeb 	if (!iwl_is_tas_approved()) {
1126d9836fb4SBjoern A. Zeeb 		IWL_DEBUG_RADIO(mvm,
1127d9836fb4SBjoern A. Zeeb 				"System vendor '%s' is not in the approved list, disabling TAS in US and Canada.\n",
1128*a4128aadSBjoern A. Zeeb 				dmi_get_system_info(DMI_SYS_VENDOR) ?: "<unknown>");
1129*a4128aadSBjoern A. Zeeb 		if ((!iwl_mvm_add_to_tas_block_list(data.block_list_array,
1130*a4128aadSBjoern A. Zeeb 						    &data.block_list_size,
1131*a4128aadSBjoern A. Zeeb 						    IWL_MCC_US)) ||
1132*a4128aadSBjoern A. Zeeb 		    (!iwl_mvm_add_to_tas_block_list(data.block_list_array,
1133*a4128aadSBjoern A. Zeeb 						    &data.block_list_size,
1134*a4128aadSBjoern A. Zeeb 						    IWL_MCC_CANADA))) {
1135d9836fb4SBjoern A. Zeeb 			IWL_DEBUG_RADIO(mvm,
1136d9836fb4SBjoern A. Zeeb 					"Unable to add US/Canada to TAS block list, disabling TAS\n");
1137d9836fb4SBjoern A. Zeeb 			return;
1138d9836fb4SBjoern A. Zeeb 		}
11399af1bba4SBjoern A. Zeeb 	} else {
11409af1bba4SBjoern A. Zeeb 		IWL_DEBUG_RADIO(mvm,
11419af1bba4SBjoern A. Zeeb 				"System vendor '%s' is in the approved list.\n",
1142*a4128aadSBjoern A. Zeeb 				dmi_get_system_info(DMI_SYS_VENDOR) ?: "<unknown>");
1143d9836fb4SBjoern A. Zeeb 	}
1144bfcc09ddSBjoern A. Zeeb 
1145*a4128aadSBjoern A. Zeeb 	fw_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id,
1146*a4128aadSBjoern A. Zeeb 				       IWL_FW_CMD_VER_UNKNOWN);
1147*a4128aadSBjoern A. Zeeb 
1148*a4128aadSBjoern A. Zeeb 	memcpy(&cmd.common, &data, sizeof(struct iwl_tas_config_cmd_common));
1149*a4128aadSBjoern A. Zeeb 
1150*a4128aadSBjoern A. Zeeb 	/* Set v3 or v4 specific parts. will be trunctated for fw_ver < 3 */
1151*a4128aadSBjoern A. Zeeb 	if (fw_ver == 4) {
1152*a4128aadSBjoern A. Zeeb 		cmd.v4.override_tas_iec = data.override_tas_iec;
1153*a4128aadSBjoern A. Zeeb 		cmd.v4.enable_tas_iec = data.enable_tas_iec;
1154*a4128aadSBjoern A. Zeeb 		cmd.v4.usa_tas_uhb_allowed = data.usa_tas_uhb_allowed;
1155*a4128aadSBjoern A. Zeeb 	} else {
1156*a4128aadSBjoern A. Zeeb 		cmd.v3.override_tas_iec = cpu_to_le16(data.override_tas_iec);
1157*a4128aadSBjoern A. Zeeb 		cmd.v3.enable_tas_iec = cpu_to_le16(data.enable_tas_iec);
1158*a4128aadSBjoern A. Zeeb 	}
1159*a4128aadSBjoern A. Zeeb 
1160*a4128aadSBjoern A. Zeeb 	cmd_size = sizeof(struct iwl_tas_config_cmd_common);
1161*a4128aadSBjoern A. Zeeb 	if (fw_ver >= 3)
1162*a4128aadSBjoern A. Zeeb 		/* v4 is the same size as v3 */
1163*a4128aadSBjoern A. Zeeb 		cmd_size += sizeof(struct iwl_tas_config_cmd_v3);
1164d9836fb4SBjoern A. Zeeb 
1165d9836fb4SBjoern A. Zeeb 	ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, cmd_size, &cmd);
1166bfcc09ddSBjoern A. Zeeb 	if (ret < 0)
1167bfcc09ddSBjoern A. Zeeb 		IWL_DEBUG_RADIO(mvm, "failed to send TAS_CONFIG (%d)\n", ret);
1168bfcc09ddSBjoern A. Zeeb }
1169bfcc09ddSBjoern A. Zeeb 
1170*a4128aadSBjoern A. Zeeb static bool iwl_mvm_eval_dsm_rfi(struct iwl_mvm *mvm)
1171bfcc09ddSBjoern A. Zeeb {
1172*a4128aadSBjoern A. Zeeb 	u32 value = 0;
1173*a4128aadSBjoern A. Zeeb 	/* default behaviour is disabled */
1174*a4128aadSBjoern A. Zeeb 	bool bios_enable_rfi = false;
1175*a4128aadSBjoern A. Zeeb 	int ret = iwl_bios_get_dsm(&mvm->fwrt, DSM_FUNC_RFI_CONFIG, &value);
1176*a4128aadSBjoern A. Zeeb 
1177bfcc09ddSBjoern A. Zeeb 
1178bfcc09ddSBjoern A. Zeeb 	if (ret < 0) {
1179bfcc09ddSBjoern A. Zeeb 		IWL_DEBUG_RADIO(mvm, "Failed to get DSM RFI, ret=%d\n", ret);
1180*a4128aadSBjoern A. Zeeb 		return bios_enable_rfi;
1181bfcc09ddSBjoern A. Zeeb 	}
1182bfcc09ddSBjoern A. Zeeb 
1183*a4128aadSBjoern A. Zeeb 	value &= DSM_VALUE_RFI_DISABLE;
1184*a4128aadSBjoern A. Zeeb 	/* RFI BIOS CONFIG value can be 0 or 3 only.
1185*a4128aadSBjoern A. Zeeb 	 * i.e 0 means DDR and DLVR enabled. 3 means DDR and DLVR disabled.
1186*a4128aadSBjoern A. Zeeb 	 * 1 and 2 are invalid BIOS configurations, So, it's not possible to
1187*a4128aadSBjoern A. Zeeb 	 * disable ddr/dlvr separately.
1188*a4128aadSBjoern A. Zeeb 	 */
1189*a4128aadSBjoern A. Zeeb 	if (!value) {
1190*a4128aadSBjoern A. Zeeb 		IWL_DEBUG_RADIO(mvm, "DSM RFI is evaluated to enable\n");
1191*a4128aadSBjoern A. Zeeb 		bios_enable_rfi = true;
1192*a4128aadSBjoern A. Zeeb 	} else if (value == DSM_VALUE_RFI_DISABLE) {
1193*a4128aadSBjoern A. Zeeb 		IWL_DEBUG_RADIO(mvm, "DSM RFI is evaluated to disable\n");
1194*a4128aadSBjoern A. Zeeb 	} else {
1195*a4128aadSBjoern A. Zeeb 		IWL_DEBUG_RADIO(mvm,
1196*a4128aadSBjoern A. Zeeb 				"DSM RFI got invalid value, value=%d\n", value);
1197*a4128aadSBjoern A. Zeeb 	}
1198bfcc09ddSBjoern A. Zeeb 
1199*a4128aadSBjoern A. Zeeb 	return bios_enable_rfi;
1200bfcc09ddSBjoern A. Zeeb }
1201bfcc09ddSBjoern A. Zeeb 
1202bfcc09ddSBjoern A. Zeeb static void iwl_mvm_lari_cfg(struct iwl_mvm *mvm)
1203bfcc09ddSBjoern A. Zeeb {
1204*a4128aadSBjoern A. Zeeb 	struct iwl_lari_config_change_cmd cmd;
1205bfcc09ddSBjoern A. Zeeb 	size_t cmd_size;
1206*a4128aadSBjoern A. Zeeb 	int ret;
1207bfcc09ddSBjoern A. Zeeb 
1208*a4128aadSBjoern A. Zeeb 	ret = iwl_fill_lari_config(&mvm->fwrt, &cmd, &cmd_size);
1209*a4128aadSBjoern A. Zeeb 	if (!ret) {
1210bfcc09ddSBjoern A. Zeeb 		ret = iwl_mvm_send_cmd_pdu(mvm,
1211bfcc09ddSBjoern A. Zeeb 					   WIDE_ID(REGULATORY_AND_NVM_GROUP,
1212bfcc09ddSBjoern A. Zeeb 						   LARI_CONFIG_CHANGE),
1213bfcc09ddSBjoern A. Zeeb 					   0, cmd_size, &cmd);
1214bfcc09ddSBjoern A. Zeeb 		if (ret < 0)
1215bfcc09ddSBjoern A. Zeeb 			IWL_DEBUG_RADIO(mvm,
1216bfcc09ddSBjoern A. Zeeb 					"Failed to send LARI_CONFIG_CHANGE (%d)\n",
1217bfcc09ddSBjoern A. Zeeb 					ret);
1218bfcc09ddSBjoern A. Zeeb 	}
1219bfcc09ddSBjoern A. Zeeb }
1220bfcc09ddSBjoern A. Zeeb 
1221*a4128aadSBjoern A. Zeeb void iwl_mvm_get_bios_tables(struct iwl_mvm *mvm)
1222bfcc09ddSBjoern A. Zeeb {
1223bfcc09ddSBjoern A. Zeeb 	int ret;
1224bfcc09ddSBjoern A. Zeeb 
1225*a4128aadSBjoern A. Zeeb 	iwl_acpi_get_guid_lock_status(&mvm->fwrt);
1226*a4128aadSBjoern A. Zeeb 
1227bfcc09ddSBjoern A. Zeeb 	/* read PPAG table */
1228*a4128aadSBjoern A. Zeeb 	ret = iwl_bios_get_ppag_table(&mvm->fwrt);
1229bfcc09ddSBjoern A. Zeeb 	if (ret < 0) {
1230bfcc09ddSBjoern A. Zeeb 		IWL_DEBUG_RADIO(mvm,
1231bfcc09ddSBjoern A. Zeeb 				"PPAG BIOS table invalid or unavailable. (%d)\n",
1232bfcc09ddSBjoern A. Zeeb 				ret);
1233bfcc09ddSBjoern A. Zeeb 	}
1234bfcc09ddSBjoern A. Zeeb 
1235bfcc09ddSBjoern A. Zeeb 	/* read SAR tables */
1236*a4128aadSBjoern A. Zeeb 	ret = iwl_bios_get_wrds_table(&mvm->fwrt);
1237bfcc09ddSBjoern A. Zeeb 	if (ret < 0) {
1238bfcc09ddSBjoern A. Zeeb 		IWL_DEBUG_RADIO(mvm,
1239bfcc09ddSBjoern A. Zeeb 				"WRDS SAR BIOS table invalid or unavailable. (%d)\n",
1240bfcc09ddSBjoern A. Zeeb 				ret);
1241bfcc09ddSBjoern A. Zeeb 		/*
1242bfcc09ddSBjoern A. Zeeb 		 * If not available, don't fail and don't bother with EWRD and
1243bfcc09ddSBjoern A. Zeeb 		 * WGDS */
1244bfcc09ddSBjoern A. Zeeb 
1245*a4128aadSBjoern A. Zeeb 		if (!iwl_bios_get_wgds_table(&mvm->fwrt)) {
1246bfcc09ddSBjoern A. Zeeb 			/*
1247bfcc09ddSBjoern A. Zeeb 			 * If basic SAR is not available, we check for WGDS,
1248bfcc09ddSBjoern A. Zeeb 			 * which should *not* be available either.  If it is
1249bfcc09ddSBjoern A. Zeeb 			 * available, issue an error, because we can't use SAR
1250bfcc09ddSBjoern A. Zeeb 			 * Geo without basic SAR.
1251bfcc09ddSBjoern A. Zeeb 			 */
1252bfcc09ddSBjoern A. Zeeb 			IWL_ERR(mvm, "BIOS contains WGDS but no WRDS\n");
1253bfcc09ddSBjoern A. Zeeb 		}
1254bfcc09ddSBjoern A. Zeeb 
1255bfcc09ddSBjoern A. Zeeb 	} else {
1256*a4128aadSBjoern A. Zeeb 		ret = iwl_bios_get_ewrd_table(&mvm->fwrt);
1257bfcc09ddSBjoern A. Zeeb 		/* if EWRD is not available, we can still use
1258bfcc09ddSBjoern A. Zeeb 		* WRDS, so don't fail */
1259bfcc09ddSBjoern A. Zeeb 		if (ret < 0)
1260bfcc09ddSBjoern A. Zeeb 			IWL_DEBUG_RADIO(mvm,
1261bfcc09ddSBjoern A. Zeeb 					"EWRD SAR BIOS table invalid or unavailable. (%d)\n",
1262bfcc09ddSBjoern A. Zeeb 					ret);
1263bfcc09ddSBjoern A. Zeeb 
1264bfcc09ddSBjoern A. Zeeb 		/* read geo SAR table */
1265bfcc09ddSBjoern A. Zeeb 		if (iwl_sar_geo_support(&mvm->fwrt)) {
1266*a4128aadSBjoern A. Zeeb 			ret = iwl_bios_get_wgds_table(&mvm->fwrt);
1267bfcc09ddSBjoern A. Zeeb 			if (ret < 0)
1268bfcc09ddSBjoern A. Zeeb 				IWL_DEBUG_RADIO(mvm,
1269bfcc09ddSBjoern A. Zeeb 						"Geo SAR BIOS table invalid or unavailable. (%d)\n",
1270bfcc09ddSBjoern A. Zeeb 						ret);
1271bfcc09ddSBjoern A. Zeeb 				/* we don't fail if the table is not available */
1272bfcc09ddSBjoern A. Zeeb 		}
1273bfcc09ddSBjoern A. Zeeb 	}
12749af1bba4SBjoern A. Zeeb 
12759af1bba4SBjoern A. Zeeb 	iwl_acpi_get_phy_filters(&mvm->fwrt, &mvm->phy_filters);
1276bfcc09ddSBjoern A. Zeeb 
1277*a4128aadSBjoern A. Zeeb 	if (iwl_bios_get_eckv(&mvm->fwrt, &mvm->ext_clock_valid))
1278*a4128aadSBjoern A. Zeeb 		IWL_DEBUG_RADIO(mvm, "ECKV table doesn't exist in BIOS\n");
1279*a4128aadSBjoern A. Zeeb }
1280*a4128aadSBjoern A. Zeeb 
1281*a4128aadSBjoern A. Zeeb static void iwl_mvm_disconnect_iterator(void *data, u8 *mac,
1282*a4128aadSBjoern A. Zeeb 					struct ieee80211_vif *vif)
1283bfcc09ddSBjoern A. Zeeb {
1284*a4128aadSBjoern A. Zeeb 	if (vif->type == NL80211_IFTYPE_STATION)
1285*a4128aadSBjoern A. Zeeb 		ieee80211_hw_restart_disconnect(vif);
1286bfcc09ddSBjoern A. Zeeb }
1287bfcc09ddSBjoern A. Zeeb 
1288bfcc09ddSBjoern A. Zeeb void iwl_mvm_send_recovery_cmd(struct iwl_mvm *mvm, u32 flags)
1289bfcc09ddSBjoern A. Zeeb {
1290bfcc09ddSBjoern A. Zeeb 	u32 error_log_size = mvm->fw->ucode_capa.error_log_size;
1291bfcc09ddSBjoern A. Zeeb 	int ret;
1292bfcc09ddSBjoern A. Zeeb 	u32 resp;
1293bfcc09ddSBjoern A. Zeeb 
1294bfcc09ddSBjoern A. Zeeb 	struct iwl_fw_error_recovery_cmd recovery_cmd = {
1295bfcc09ddSBjoern A. Zeeb 		.flags = cpu_to_le32(flags),
1296bfcc09ddSBjoern A. Zeeb 		.buf_size = 0,
1297bfcc09ddSBjoern A. Zeeb 	};
1298bfcc09ddSBjoern A. Zeeb 	struct iwl_host_cmd host_cmd = {
1299bfcc09ddSBjoern A. Zeeb 		.id = WIDE_ID(SYSTEM_GROUP, FW_ERROR_RECOVERY_CMD),
1300bfcc09ddSBjoern A. Zeeb 		.flags = CMD_WANT_SKB,
1301bfcc09ddSBjoern A. Zeeb 		.data = {&recovery_cmd, },
1302bfcc09ddSBjoern A. Zeeb 		.len = {sizeof(recovery_cmd), },
1303bfcc09ddSBjoern A. Zeeb 	};
1304bfcc09ddSBjoern A. Zeeb 
1305bfcc09ddSBjoern A. Zeeb 	/* no error log was defined in TLV */
1306bfcc09ddSBjoern A. Zeeb 	if (!error_log_size)
1307bfcc09ddSBjoern A. Zeeb 		return;
1308bfcc09ddSBjoern A. Zeeb 
1309bfcc09ddSBjoern A. Zeeb 	if (flags & ERROR_RECOVERY_UPDATE_DB) {
1310bfcc09ddSBjoern A. Zeeb 		/* no buf was allocated while HW reset */
1311bfcc09ddSBjoern A. Zeeb 		if (!mvm->error_recovery_buf)
1312bfcc09ddSBjoern A. Zeeb 			return;
1313bfcc09ddSBjoern A. Zeeb 
1314bfcc09ddSBjoern A. Zeeb 		host_cmd.data[1] = mvm->error_recovery_buf;
1315bfcc09ddSBjoern A. Zeeb 		host_cmd.len[1] =  error_log_size;
1316bfcc09ddSBjoern A. Zeeb 		host_cmd.dataflags[1] = IWL_HCMD_DFL_NOCOPY;
1317bfcc09ddSBjoern A. Zeeb 		recovery_cmd.buf_size = cpu_to_le32(error_log_size);
1318bfcc09ddSBjoern A. Zeeb 	}
1319bfcc09ddSBjoern A. Zeeb 
1320bfcc09ddSBjoern A. Zeeb 	ret = iwl_mvm_send_cmd(mvm, &host_cmd);
1321bfcc09ddSBjoern A. Zeeb 	kfree(mvm->error_recovery_buf);
1322bfcc09ddSBjoern A. Zeeb 	mvm->error_recovery_buf = NULL;
1323bfcc09ddSBjoern A. Zeeb 
1324bfcc09ddSBjoern A. Zeeb 	if (ret) {
1325bfcc09ddSBjoern A. Zeeb 		IWL_ERR(mvm, "Failed to send recovery cmd %d\n", ret);
1326bfcc09ddSBjoern A. Zeeb 		return;
1327bfcc09ddSBjoern A. Zeeb 	}
1328bfcc09ddSBjoern A. Zeeb 
1329bfcc09ddSBjoern A. Zeeb 	/* skb respond is only relevant in ERROR_RECOVERY_UPDATE_DB */
1330bfcc09ddSBjoern A. Zeeb 	if (flags & ERROR_RECOVERY_UPDATE_DB) {
1331bfcc09ddSBjoern A. Zeeb 		resp = le32_to_cpu(*(__le32 *)host_cmd.resp_pkt->data);
1332*a4128aadSBjoern A. Zeeb 		if (resp) {
1333bfcc09ddSBjoern A. Zeeb 			IWL_ERR(mvm,
1334bfcc09ddSBjoern A. Zeeb 				"Failed to send recovery cmd blob was invalid %d\n",
1335bfcc09ddSBjoern A. Zeeb 				resp);
1336*a4128aadSBjoern A. Zeeb 
1337*a4128aadSBjoern A. Zeeb 			ieee80211_iterate_interfaces(mvm->hw, 0,
1338*a4128aadSBjoern A. Zeeb 						     iwl_mvm_disconnect_iterator,
1339*a4128aadSBjoern A. Zeeb 						     mvm);
1340*a4128aadSBjoern A. Zeeb 		}
1341bfcc09ddSBjoern A. Zeeb 	}
1342bfcc09ddSBjoern A. Zeeb }
1343bfcc09ddSBjoern A. Zeeb 
1344bfcc09ddSBjoern A. Zeeb static int iwl_mvm_sar_init(struct iwl_mvm *mvm)
1345bfcc09ddSBjoern A. Zeeb {
1346bfcc09ddSBjoern A. Zeeb 	return iwl_mvm_sar_select_profile(mvm, 1, 1);
1347bfcc09ddSBjoern A. Zeeb }
1348bfcc09ddSBjoern A. Zeeb 
1349bfcc09ddSBjoern A. Zeeb static int iwl_mvm_load_rt_fw(struct iwl_mvm *mvm)
1350bfcc09ddSBjoern A. Zeeb {
1351bfcc09ddSBjoern A. Zeeb 	int ret;
1352bfcc09ddSBjoern A. Zeeb 
1353bfcc09ddSBjoern A. Zeeb 	if (iwl_mvm_has_unified_ucode(mvm))
1354bfcc09ddSBjoern A. Zeeb 		return iwl_run_unified_mvm_ucode(mvm);
1355bfcc09ddSBjoern A. Zeeb 
1356bfcc09ddSBjoern A. Zeeb 	ret = iwl_run_init_mvm_ucode(mvm);
1357bfcc09ddSBjoern A. Zeeb 
1358bfcc09ddSBjoern A. Zeeb 	if (ret) {
1359bfcc09ddSBjoern A. Zeeb 		IWL_ERR(mvm, "Failed to run INIT ucode: %d\n", ret);
1360bfcc09ddSBjoern A. Zeeb 		return ret;
1361bfcc09ddSBjoern A. Zeeb 	}
1362bfcc09ddSBjoern A. Zeeb 
1363bfcc09ddSBjoern A. Zeeb 	iwl_fw_dbg_stop_sync(&mvm->fwrt);
1364bfcc09ddSBjoern A. Zeeb 	iwl_trans_stop_device(mvm->trans);
1365bfcc09ddSBjoern A. Zeeb 	ret = iwl_trans_start_hw(mvm->trans);
1366bfcc09ddSBjoern A. Zeeb 	if (ret)
1367bfcc09ddSBjoern A. Zeeb 		return ret;
1368bfcc09ddSBjoern A. Zeeb 
1369bfcc09ddSBjoern A. Zeeb 	mvm->rfkill_safe_init_done = false;
1370bfcc09ddSBjoern A. Zeeb 	ret = iwl_mvm_load_ucode_wait_alive(mvm, IWL_UCODE_REGULAR);
1371bfcc09ddSBjoern A. Zeeb 	if (ret)
1372bfcc09ddSBjoern A. Zeeb 		return ret;
1373bfcc09ddSBjoern A. Zeeb 
1374bfcc09ddSBjoern A. Zeeb 	mvm->rfkill_safe_init_done = true;
1375bfcc09ddSBjoern A. Zeeb 
1376bfcc09ddSBjoern A. Zeeb 	iwl_dbg_tlv_time_point(&mvm->fwrt, IWL_FW_INI_TIME_POINT_AFTER_ALIVE,
1377bfcc09ddSBjoern A. Zeeb 			       NULL);
1378bfcc09ddSBjoern A. Zeeb 
1379bfcc09ddSBjoern A. Zeeb 	return iwl_init_paging(&mvm->fwrt, mvm->fwrt.cur_fw_img);
1380bfcc09ddSBjoern A. Zeeb }
1381bfcc09ddSBjoern A. Zeeb 
1382bfcc09ddSBjoern A. Zeeb int iwl_mvm_up(struct iwl_mvm *mvm)
1383bfcc09ddSBjoern A. Zeeb {
1384bfcc09ddSBjoern A. Zeeb 	int ret, i;
1385bfcc09ddSBjoern A. Zeeb 	struct ieee80211_supported_band *sband = NULL;
1386bfcc09ddSBjoern A. Zeeb 
1387bfcc09ddSBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
1388bfcc09ddSBjoern A. Zeeb 
1389bfcc09ddSBjoern A. Zeeb 	ret = iwl_trans_start_hw(mvm->trans);
1390bfcc09ddSBjoern A. Zeeb 	if (ret)
1391bfcc09ddSBjoern A. Zeeb 		return ret;
1392bfcc09ddSBjoern A. Zeeb 
1393bfcc09ddSBjoern A. Zeeb 	ret = iwl_mvm_load_rt_fw(mvm);
1394bfcc09ddSBjoern A. Zeeb 	if (ret) {
1395bfcc09ddSBjoern A. Zeeb 		IWL_ERR(mvm, "Failed to start RT ucode: %d\n", ret);
1396*a4128aadSBjoern A. Zeeb 		if (ret != -ERFKILL && !mvm->fw_product_reset)
1397bfcc09ddSBjoern A. Zeeb 			iwl_fw_dbg_error_collect(&mvm->fwrt,
1398bfcc09ddSBjoern A. Zeeb 						 FW_DBG_TRIGGER_DRIVER);
1399bfcc09ddSBjoern A. Zeeb 		goto error;
1400bfcc09ddSBjoern A. Zeeb 	}
1401bfcc09ddSBjoern A. Zeeb 
14029af1bba4SBjoern A. Zeeb 	/* FW loaded successfully */
1403*a4128aadSBjoern A. Zeeb 	mvm->fw_product_reset = false;
14049af1bba4SBjoern A. Zeeb 
1405*a4128aadSBjoern A. Zeeb 	iwl_fw_disable_dbg_asserts(&mvm->fwrt);
1406bfcc09ddSBjoern A. Zeeb 	iwl_get_shared_mem_conf(&mvm->fwrt);
1407bfcc09ddSBjoern A. Zeeb 
1408bfcc09ddSBjoern A. Zeeb 	ret = iwl_mvm_sf_update(mvm, NULL, false);
1409bfcc09ddSBjoern A. Zeeb 	if (ret)
1410bfcc09ddSBjoern A. Zeeb 		IWL_ERR(mvm, "Failed to initialize Smart Fifo\n");
1411bfcc09ddSBjoern A. Zeeb 
1412bfcc09ddSBjoern A. Zeeb 	if (!iwl_trans_dbg_ini_valid(mvm->trans)) {
1413bfcc09ddSBjoern A. Zeeb 		mvm->fwrt.dump.conf = FW_DBG_INVALID;
1414bfcc09ddSBjoern A. Zeeb 		/* if we have a destination, assume EARLY START */
1415bfcc09ddSBjoern A. Zeeb 		if (mvm->fw->dbg.dest_tlv)
1416bfcc09ddSBjoern A. Zeeb 			mvm->fwrt.dump.conf = FW_DBG_START_FROM_ALIVE;
1417bfcc09ddSBjoern A. Zeeb 		iwl_fw_start_dbg_conf(&mvm->fwrt, FW_DBG_START_FROM_ALIVE);
1418bfcc09ddSBjoern A. Zeeb 	}
1419bfcc09ddSBjoern A. Zeeb 
1420bfcc09ddSBjoern A. Zeeb 	ret = iwl_send_tx_ant_cfg(mvm, iwl_mvm_get_valid_tx_ant(mvm));
1421bfcc09ddSBjoern A. Zeeb 	if (ret)
1422bfcc09ddSBjoern A. Zeeb 		goto error;
1423bfcc09ddSBjoern A. Zeeb 
1424bfcc09ddSBjoern A. Zeeb 	if (!iwl_mvm_has_unified_ucode(mvm)) {
1425bfcc09ddSBjoern A. Zeeb 		/* Send phy db control command and then phy db calibration */
1426bfcc09ddSBjoern A. Zeeb 		ret = iwl_send_phy_db_data(mvm->phy_db);
1427bfcc09ddSBjoern A. Zeeb 		if (ret)
1428bfcc09ddSBjoern A. Zeeb 			goto error;
1429bfcc09ddSBjoern A. Zeeb 		ret = iwl_send_phy_cfg_cmd(mvm);
1430bfcc09ddSBjoern A. Zeeb 		if (ret)
1431bfcc09ddSBjoern A. Zeeb 			goto error;
14329af1bba4SBjoern A. Zeeb 	}
1433bfcc09ddSBjoern A. Zeeb 
1434bfcc09ddSBjoern A. Zeeb 	ret = iwl_mvm_send_bt_init_conf(mvm);
1435bfcc09ddSBjoern A. Zeeb 	if (ret)
1436bfcc09ddSBjoern A. Zeeb 		goto error;
1437bfcc09ddSBjoern A. Zeeb 
1438bfcc09ddSBjoern A. Zeeb 	if (fw_has_capa(&mvm->fw->ucode_capa,
1439bfcc09ddSBjoern A. Zeeb 			IWL_UCODE_TLV_CAPA_SOC_LATENCY_SUPPORT)) {
1440bfcc09ddSBjoern A. Zeeb 		ret = iwl_set_soc_latency(&mvm->fwrt);
1441bfcc09ddSBjoern A. Zeeb 		if (ret)
1442bfcc09ddSBjoern A. Zeeb 			goto error;
1443bfcc09ddSBjoern A. Zeeb 	}
1444bfcc09ddSBjoern A. Zeeb 
14459af1bba4SBjoern A. Zeeb 	iwl_mvm_lari_cfg(mvm);
14469af1bba4SBjoern A. Zeeb 
1447bfcc09ddSBjoern A. Zeeb 	/* Init RSS configuration */
1448bfcc09ddSBjoern A. Zeeb 	ret = iwl_configure_rxq(&mvm->fwrt);
1449bfcc09ddSBjoern A. Zeeb 	if (ret)
1450bfcc09ddSBjoern A. Zeeb 		goto error;
1451bfcc09ddSBjoern A. Zeeb 
1452bfcc09ddSBjoern A. Zeeb 	if (iwl_mvm_has_new_rx_api(mvm)) {
1453bfcc09ddSBjoern A. Zeeb 		ret = iwl_send_rss_cfg_cmd(mvm);
1454bfcc09ddSBjoern A. Zeeb 		if (ret) {
1455bfcc09ddSBjoern A. Zeeb 			IWL_ERR(mvm, "Failed to configure RSS queues: %d\n",
1456bfcc09ddSBjoern A. Zeeb 				ret);
1457bfcc09ddSBjoern A. Zeeb 			goto error;
1458bfcc09ddSBjoern A. Zeeb 		}
1459bfcc09ddSBjoern A. Zeeb 	}
1460bfcc09ddSBjoern A. Zeeb 
1461bfcc09ddSBjoern A. Zeeb 	/* init the fw <-> mac80211 STA mapping */
14629af1bba4SBjoern A. Zeeb 	for (i = 0; i < mvm->fw->ucode_capa.num_stations; i++) {
1463bfcc09ddSBjoern A. Zeeb 		RCU_INIT_POINTER(mvm->fw_id_to_mac_id[i], NULL);
14649af1bba4SBjoern A. Zeeb 		RCU_INIT_POINTER(mvm->fw_id_to_link_sta[i], NULL);
14659af1bba4SBjoern A. Zeeb 	}
14669af1bba4SBjoern A. Zeeb 
14679af1bba4SBjoern A. Zeeb 	for (i = 0; i < IWL_MVM_FW_MAX_LINK_ID + 1; i++)
14689af1bba4SBjoern A. Zeeb 		RCU_INIT_POINTER(mvm->link_id_to_link_conf[i], NULL);
14699af1bba4SBjoern A. Zeeb 
1470bfcc09ddSBjoern A. Zeeb 	mvm->tdls_cs.peer.sta_id = IWL_MVM_INVALID_STA;
1471bfcc09ddSBjoern A. Zeeb 
1472bfcc09ddSBjoern A. Zeeb 	/* reset quota debouncing buffer - 0xff will yield invalid data */
1473bfcc09ddSBjoern A. Zeeb 	memset(&mvm->last_quota_cmd, 0xff, sizeof(mvm->last_quota_cmd));
1474bfcc09ddSBjoern A. Zeeb 
1475bfcc09ddSBjoern A. Zeeb 	if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_DQA_SUPPORT)) {
1476bfcc09ddSBjoern A. Zeeb 		ret = iwl_mvm_send_dqa_cmd(mvm);
1477bfcc09ddSBjoern A. Zeeb 		if (ret)
1478bfcc09ddSBjoern A. Zeeb 			goto error;
1479bfcc09ddSBjoern A. Zeeb 	}
1480bfcc09ddSBjoern A. Zeeb 
1481bfcc09ddSBjoern A. Zeeb 	/*
1482bfcc09ddSBjoern A. Zeeb 	 * Add auxiliary station for scanning.
1483bfcc09ddSBjoern A. Zeeb 	 * Newer versions of this command implies that the fw uses
1484bfcc09ddSBjoern A. Zeeb 	 * internal aux station for all aux activities that don't
1485bfcc09ddSBjoern A. Zeeb 	 * requires a dedicated data queue.
1486bfcc09ddSBjoern A. Zeeb 	 */
14879af1bba4SBjoern A. Zeeb 	if (!iwl_mvm_has_new_station_api(mvm->fw)) {
1488bfcc09ddSBjoern A. Zeeb 		 /*
1489bfcc09ddSBjoern A. Zeeb 		  * In old version the aux station uses mac id like other
1490bfcc09ddSBjoern A. Zeeb 		  * station and not lmac id
1491bfcc09ddSBjoern A. Zeeb 		  */
1492bfcc09ddSBjoern A. Zeeb 		ret = iwl_mvm_add_aux_sta(mvm, MAC_INDEX_AUX);
1493bfcc09ddSBjoern A. Zeeb 		if (ret)
1494bfcc09ddSBjoern A. Zeeb 			goto error;
1495bfcc09ddSBjoern A. Zeeb 	}
1496bfcc09ddSBjoern A. Zeeb 
1497bfcc09ddSBjoern A. Zeeb 	/* Add all the PHY contexts */
1498bfcc09ddSBjoern A. Zeeb 	i = 0;
1499bfcc09ddSBjoern A. Zeeb 	while (!sband && i < NUM_NL80211_BANDS)
1500bfcc09ddSBjoern A. Zeeb 		sband = mvm->hw->wiphy->bands[i++];
1501bfcc09ddSBjoern A. Zeeb 
1502d9836fb4SBjoern A. Zeeb 	if (WARN_ON_ONCE(!sband)) {
1503d9836fb4SBjoern A. Zeeb 		ret = -ENODEV;
1504bfcc09ddSBjoern A. Zeeb 		goto error;
1505d9836fb4SBjoern A. Zeeb 	}
1506bfcc09ddSBjoern A. Zeeb 
1507bfcc09ddSBjoern A. Zeeb 	if (iwl_mvm_is_tt_in_fw(mvm)) {
1508bfcc09ddSBjoern A. Zeeb 		/* in order to give the responsibility of ct-kill and
1509bfcc09ddSBjoern A. Zeeb 		 * TX backoff to FW we need to send empty temperature reporting
1510bfcc09ddSBjoern A. Zeeb 		 * cmd during init time
1511bfcc09ddSBjoern A. Zeeb 		 */
1512bfcc09ddSBjoern A. Zeeb 		iwl_mvm_send_temp_report_ths_cmd(mvm);
1513bfcc09ddSBjoern A. Zeeb 	} else {
1514bfcc09ddSBjoern A. Zeeb 		/* Initialize tx backoffs to the minimal possible */
1515bfcc09ddSBjoern A. Zeeb 		iwl_mvm_tt_tx_backoff(mvm, 0);
1516bfcc09ddSBjoern A. Zeeb 	}
1517bfcc09ddSBjoern A. Zeeb 
1518bfcc09ddSBjoern A. Zeeb #ifdef CONFIG_THERMAL
1519bfcc09ddSBjoern A. Zeeb 	/* TODO: read the budget from BIOS / Platform NVM */
1520bfcc09ddSBjoern A. Zeeb 
1521bfcc09ddSBjoern A. Zeeb 	/*
1522bfcc09ddSBjoern A. Zeeb 	 * In case there is no budget from BIOS / Platform NVM the default
1523bfcc09ddSBjoern A. Zeeb 	 * budget should be 2000mW (cooling state 0).
1524bfcc09ddSBjoern A. Zeeb 	 */
1525bfcc09ddSBjoern A. Zeeb 	if (iwl_mvm_is_ctdp_supported(mvm)) {
1526bfcc09ddSBjoern A. Zeeb 		ret = iwl_mvm_ctdp_command(mvm, CTDP_CMD_OPERATION_START,
1527bfcc09ddSBjoern A. Zeeb 					   mvm->cooling_dev.cur_state);
1528bfcc09ddSBjoern A. Zeeb 		if (ret)
1529bfcc09ddSBjoern A. Zeeb 			goto error;
1530bfcc09ddSBjoern A. Zeeb 	}
1531bfcc09ddSBjoern A. Zeeb #endif
1532bfcc09ddSBjoern A. Zeeb 
1533bfcc09ddSBjoern A. Zeeb 	if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_SET_LTR_GEN2))
1534bfcc09ddSBjoern A. Zeeb 		WARN_ON(iwl_mvm_config_ltr(mvm));
1535bfcc09ddSBjoern A. Zeeb 
1536bfcc09ddSBjoern A. Zeeb 	ret = iwl_mvm_power_update_device(mvm);
1537bfcc09ddSBjoern A. Zeeb 	if (ret)
1538bfcc09ddSBjoern A. Zeeb 		goto error;
1539bfcc09ddSBjoern A. Zeeb 
1540bfcc09ddSBjoern A. Zeeb 	/*
1541bfcc09ddSBjoern A. Zeeb 	 * RTNL is not taken during Ct-kill, but we don't need to scan/Tx
1542bfcc09ddSBjoern A. Zeeb 	 * anyway, so don't init MCC.
1543bfcc09ddSBjoern A. Zeeb 	 */
1544bfcc09ddSBjoern A. Zeeb 	if (!test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status)) {
1545bfcc09ddSBjoern A. Zeeb 		ret = iwl_mvm_init_mcc(mvm);
1546bfcc09ddSBjoern A. Zeeb 		if (ret)
1547bfcc09ddSBjoern A. Zeeb 			goto error;
1548bfcc09ddSBjoern A. Zeeb 	}
1549bfcc09ddSBjoern A. Zeeb 
1550bfcc09ddSBjoern A. Zeeb 	if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) {
1551bfcc09ddSBjoern A. Zeeb 		mvm->scan_type = IWL_SCAN_TYPE_NOT_SET;
1552bfcc09ddSBjoern A. Zeeb 		mvm->hb_scan_type = IWL_SCAN_TYPE_NOT_SET;
1553bfcc09ddSBjoern A. Zeeb 		ret = iwl_mvm_config_scan(mvm);
1554bfcc09ddSBjoern A. Zeeb 		if (ret)
1555bfcc09ddSBjoern A. Zeeb 			goto error;
1556bfcc09ddSBjoern A. Zeeb 	}
1557bfcc09ddSBjoern A. Zeeb 
15589af1bba4SBjoern A. Zeeb 	if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
1559bfcc09ddSBjoern A. Zeeb 		iwl_mvm_send_recovery_cmd(mvm, ERROR_RECOVERY_UPDATE_DB);
1560bfcc09ddSBjoern A. Zeeb 
15619af1bba4SBjoern A. Zeeb 		if (mvm->time_sync.active)
15629af1bba4SBjoern A. Zeeb 			iwl_mvm_time_sync_config(mvm, mvm->time_sync.peer_addr,
15639af1bba4SBjoern A. Zeeb 						 IWL_TIME_SYNC_PROTOCOL_TM |
15649af1bba4SBjoern A. Zeeb 						 IWL_TIME_SYNC_PROTOCOL_FTM);
15659af1bba4SBjoern A. Zeeb 	}
15669af1bba4SBjoern A. Zeeb 
15679af1bba4SBjoern A. Zeeb 	if (!mvm->ptp_data.ptp_clock)
15689af1bba4SBjoern A. Zeeb 		iwl_mvm_ptp_init(mvm);
15699af1bba4SBjoern A. Zeeb 
1570bfcc09ddSBjoern A. Zeeb 	ret = iwl_mvm_ppag_init(mvm);
1571bfcc09ddSBjoern A. Zeeb 	if (ret)
1572bfcc09ddSBjoern A. Zeeb 		goto error;
1573bfcc09ddSBjoern A. Zeeb 
1574bfcc09ddSBjoern A. Zeeb 	ret = iwl_mvm_sar_init(mvm);
1575bfcc09ddSBjoern A. Zeeb 	if (ret == 0)
1576bfcc09ddSBjoern A. Zeeb 		ret = iwl_mvm_sar_geo_init(mvm);
1577fac1f593SBjoern A. Zeeb 	if (ret < 0)
1578bfcc09ddSBjoern A. Zeeb 		goto error;
1579bfcc09ddSBjoern A. Zeeb 
1580d9836fb4SBjoern A. Zeeb 	ret = iwl_mvm_sgom_init(mvm);
1581d9836fb4SBjoern A. Zeeb 	if (ret)
1582d9836fb4SBjoern A. Zeeb 		goto error;
1583d9836fb4SBjoern A. Zeeb 
1584bfcc09ddSBjoern A. Zeeb 	iwl_mvm_tas_init(mvm);
1585bfcc09ddSBjoern A. Zeeb 	iwl_mvm_leds_sync(mvm);
1586*a4128aadSBjoern A. Zeeb 	iwl_mvm_uats_init(mvm);
1587bfcc09ddSBjoern A. Zeeb 
15889af1bba4SBjoern A. Zeeb 	if (iwl_rfi_supported(mvm)) {
1589*a4128aadSBjoern A. Zeeb 		if (iwl_mvm_eval_dsm_rfi(mvm))
1590bfcc09ddSBjoern A. Zeeb 			iwl_rfi_send_config_cmd(mvm, NULL);
1591bfcc09ddSBjoern A. Zeeb 	}
1592bfcc09ddSBjoern A. Zeeb 
15939af1bba4SBjoern A. Zeeb 	iwl_mvm_mei_device_state(mvm, true);
15949af1bba4SBjoern A. Zeeb 
1595bfcc09ddSBjoern A. Zeeb 	IWL_DEBUG_INFO(mvm, "RT uCode started.\n");
1596bfcc09ddSBjoern A. Zeeb 	return 0;
1597bfcc09ddSBjoern A. Zeeb  error:
1598bfcc09ddSBjoern A. Zeeb 	iwl_mvm_stop_device(mvm);
1599bfcc09ddSBjoern A. Zeeb 	return ret;
1600bfcc09ddSBjoern A. Zeeb }
1601bfcc09ddSBjoern A. Zeeb 
1602bfcc09ddSBjoern A. Zeeb int iwl_mvm_load_d3_fw(struct iwl_mvm *mvm)
1603bfcc09ddSBjoern A. Zeeb {
1604bfcc09ddSBjoern A. Zeeb 	int ret, i;
1605bfcc09ddSBjoern A. Zeeb 
1606bfcc09ddSBjoern A. Zeeb 	lockdep_assert_held(&mvm->mutex);
1607bfcc09ddSBjoern A. Zeeb 
1608bfcc09ddSBjoern A. Zeeb 	ret = iwl_trans_start_hw(mvm->trans);
1609bfcc09ddSBjoern A. Zeeb 	if (ret)
1610bfcc09ddSBjoern A. Zeeb 		return ret;
1611bfcc09ddSBjoern A. Zeeb 
1612bfcc09ddSBjoern A. Zeeb 	ret = iwl_mvm_load_ucode_wait_alive(mvm, IWL_UCODE_WOWLAN);
1613bfcc09ddSBjoern A. Zeeb 	if (ret) {
1614bfcc09ddSBjoern A. Zeeb 		IWL_ERR(mvm, "Failed to start WoWLAN firmware: %d\n", ret);
1615bfcc09ddSBjoern A. Zeeb 		goto error;
1616bfcc09ddSBjoern A. Zeeb 	}
1617bfcc09ddSBjoern A. Zeeb 
1618bfcc09ddSBjoern A. Zeeb 	ret = iwl_send_tx_ant_cfg(mvm, iwl_mvm_get_valid_tx_ant(mvm));
1619bfcc09ddSBjoern A. Zeeb 	if (ret)
1620bfcc09ddSBjoern A. Zeeb 		goto error;
1621bfcc09ddSBjoern A. Zeeb 
1622bfcc09ddSBjoern A. Zeeb 	/* Send phy db control command and then phy db calibration*/
1623bfcc09ddSBjoern A. Zeeb 	ret = iwl_send_phy_db_data(mvm->phy_db);
1624bfcc09ddSBjoern A. Zeeb 	if (ret)
1625bfcc09ddSBjoern A. Zeeb 		goto error;
1626bfcc09ddSBjoern A. Zeeb 
1627bfcc09ddSBjoern A. Zeeb 	ret = iwl_send_phy_cfg_cmd(mvm);
1628bfcc09ddSBjoern A. Zeeb 	if (ret)
1629bfcc09ddSBjoern A. Zeeb 		goto error;
1630bfcc09ddSBjoern A. Zeeb 
1631bfcc09ddSBjoern A. Zeeb 	/* init the fw <-> mac80211 STA mapping */
16329af1bba4SBjoern A. Zeeb 	for (i = 0; i < mvm->fw->ucode_capa.num_stations; i++) {
1633bfcc09ddSBjoern A. Zeeb 		RCU_INIT_POINTER(mvm->fw_id_to_mac_id[i], NULL);
16349af1bba4SBjoern A. Zeeb 		RCU_INIT_POINTER(mvm->fw_id_to_link_sta[i], NULL);
16359af1bba4SBjoern A. Zeeb 	}
1636bfcc09ddSBjoern A. Zeeb 
16379af1bba4SBjoern A. Zeeb 	if (!iwl_mvm_has_new_station_api(mvm->fw)) {
1638bfcc09ddSBjoern A. Zeeb 		/*
1639bfcc09ddSBjoern A. Zeeb 		 * Add auxiliary station for scanning.
1640bfcc09ddSBjoern A. Zeeb 		 * Newer versions of this command implies that the fw uses
1641bfcc09ddSBjoern A. Zeeb 		 * internal aux station for all aux activities that don't
1642bfcc09ddSBjoern A. Zeeb 		 * requires a dedicated data queue.
1643bfcc09ddSBjoern A. Zeeb 		 * In old version the aux station uses mac id like other
1644bfcc09ddSBjoern A. Zeeb 		 * station and not lmac id
1645bfcc09ddSBjoern A. Zeeb 		 */
1646bfcc09ddSBjoern A. Zeeb 		ret = iwl_mvm_add_aux_sta(mvm, MAC_INDEX_AUX);
1647bfcc09ddSBjoern A. Zeeb 		if (ret)
1648bfcc09ddSBjoern A. Zeeb 			goto error;
1649bfcc09ddSBjoern A. Zeeb 	}
1650bfcc09ddSBjoern A. Zeeb 
1651bfcc09ddSBjoern A. Zeeb 	return 0;
1652bfcc09ddSBjoern A. Zeeb  error:
1653bfcc09ddSBjoern A. Zeeb 	iwl_mvm_stop_device(mvm);
1654bfcc09ddSBjoern A. Zeeb 	return ret;
1655bfcc09ddSBjoern A. Zeeb }
1656bfcc09ddSBjoern A. Zeeb 
1657bfcc09ddSBjoern A. Zeeb void iwl_mvm_rx_mfuart_notif(struct iwl_mvm *mvm,
1658bfcc09ddSBjoern A. Zeeb 			     struct iwl_rx_cmd_buffer *rxb)
1659bfcc09ddSBjoern A. Zeeb {
1660bfcc09ddSBjoern A. Zeeb 	struct iwl_rx_packet *pkt = rxb_addr(rxb);
1661bfcc09ddSBjoern A. Zeeb 	struct iwl_mfuart_load_notif *mfuart_notif = (void *)pkt->data;
1662bfcc09ddSBjoern A. Zeeb 
1663bfcc09ddSBjoern A. Zeeb 	IWL_DEBUG_INFO(mvm,
1664bfcc09ddSBjoern A. Zeeb 		       "MFUART: installed ver: 0x%08x, external ver: 0x%08x, status: 0x%08x, duration: 0x%08x\n",
1665bfcc09ddSBjoern A. Zeeb 		       le32_to_cpu(mfuart_notif->installed_ver),
1666bfcc09ddSBjoern A. Zeeb 		       le32_to_cpu(mfuart_notif->external_ver),
1667bfcc09ddSBjoern A. Zeeb 		       le32_to_cpu(mfuart_notif->status),
1668bfcc09ddSBjoern A. Zeeb 		       le32_to_cpu(mfuart_notif->duration));
1669bfcc09ddSBjoern A. Zeeb 
1670bfcc09ddSBjoern A. Zeeb 	if (iwl_rx_packet_payload_len(pkt) == sizeof(*mfuart_notif))
1671bfcc09ddSBjoern A. Zeeb 		IWL_DEBUG_INFO(mvm,
1672bfcc09ddSBjoern A. Zeeb 			       "MFUART: image size: 0x%08x\n",
1673bfcc09ddSBjoern A. Zeeb 			       le32_to_cpu(mfuart_notif->image_size));
1674bfcc09ddSBjoern A. Zeeb }
1675