File Index
Files and directories
Sign in
FILE

ucode.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/wireless/intel/iwlwifi/dvm/ucode.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/******************************************************************************
  3 *
  4 * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
  5 * Copyright(c) 2015 Intel Deutschland GmbH
  6 * Copyright (C) 2025 Intel Corporation
  7 *****************************************************************************/
  8
  9#include <linux/kernel.h>
 10
 11#include "iwl-io.h"
 12#include "iwl-agn-hw.h"
 13#include "iwl-trans.h"
 14#include "iwl-fh.h"
 15#include "iwl-op-mode.h"
 16
 17#include "dev.h"
 18#include "agn.h"
 19#include "calib.h"
 20
 21/******************************************************************************
 22 *
 23 * uCode download functions
 24 *
 25 ******************************************************************************/
 26
 27/*
 28 *  Calibration
 29 */
 30static int iwl_set_Xtal_calib(struct iwl_priv *priv)
 31{
 32	struct iwl_calib_xtal_freq_cmd cmd;
 33	__le16 *xtal_calib = priv->nvm_data->xtal_calib;
 34
 35	iwl_set_calib_hdr(&cmd.hdr, IWL_PHY_CALIBRATE_CRYSTAL_FRQ_CMD);
 36	cmd.cap_pin1 = le16_to_cpu(xtal_calib[0]);
 37	cmd.cap_pin2 = le16_to_cpu(xtal_calib[1]);
 38	return iwl_calib_set(priv, (void *)&cmd, sizeof(cmd));
 39}
 40
 41static int iwl_set_temperature_offset_calib(struct iwl_priv *priv)
 42{
 43	struct iwl_calib_temperature_offset_cmd cmd;
 44
 45	memset(&cmd, 0, sizeof(cmd));
 46	iwl_set_calib_hdr(&cmd.hdr, IWL_PHY_CALIBRATE_TEMP_OFFSET_CMD);
 47	cmd.radio_sensor_offset = priv->nvm_data->raw_temperature;
 48	if (!(cmd.radio_sensor_offset))
 49		cmd.radio_sensor_offset = DEFAULT_RADIO_SENSOR_OFFSET;
 50
 51	IWL_DEBUG_CALIB(priv, "Radio sensor offset: %d\n",
 52			le16_to_cpu(cmd.radio_sensor_offset));
 53	return iwl_calib_set(priv, (void *)&cmd, sizeof(cmd));
 54}
 55
 56static int iwl_set_temperature_offset_calib_v2(struct iwl_priv *priv)
 57{
 58	struct iwl_calib_temperature_offset_v2_cmd cmd;
 59
 60	memset(&cmd, 0, sizeof(cmd));
 61	iwl_set_calib_hdr(&cmd.hdr, IWL_PHY_CALIBRATE_TEMP_OFFSET_CMD);
 62	cmd.radio_sensor_offset_high = priv->nvm_data->kelvin_temperature;
 63	cmd.radio_sensor_offset_low = priv->nvm_data->raw_temperature;
 64	if (!cmd.radio_sensor_offset_low) {
 65		IWL_DEBUG_CALIB(priv, "no info in EEPROM, use default\n");
 66		cmd.radio_sensor_offset_low = DEFAULT_RADIO_SENSOR_OFFSET;
 67		cmd.radio_sensor_offset_high = DEFAULT_RADIO_SENSOR_OFFSET;
 68	}
 69	cmd.burntVoltageRef = priv->nvm_data->calib_voltage;
 70
 71	IWL_DEBUG_CALIB(priv, "Radio sensor offset high: %d\n",
 72			le16_to_cpu(cmd.radio_sensor_offset_high));
 73	IWL_DEBUG_CALIB(priv, "Radio sensor offset low: %d\n",
 74			le16_to_cpu(cmd.radio_sensor_offset_low));
 75	IWL_DEBUG_CALIB(priv, "Voltage Ref: %d\n",
 76			le16_to_cpu(cmd.burntVoltageRef));
 77
 78	return iwl_calib_set(priv, (void *)&cmd, sizeof(cmd));
 79}
 80
 81static int iwl_send_calib_cfg(struct iwl_priv *priv)
 82{
 83	struct iwl_calib_cfg_cmd calib_cfg_cmd;
 84	struct iwl_host_cmd cmd = {
 85		.id = CALIBRATION_CFG_CMD,
 86		.len = { sizeof(struct iwl_calib_cfg_cmd), },
 87		.data = { &calib_cfg_cmd, },
 88	};
 89
 90	memset(&calib_cfg_cmd, 0, sizeof(calib_cfg_cmd));
 91	calib_cfg_cmd.ucd_calib_cfg.once.is_enable = IWL_CALIB_INIT_CFG_ALL;
 92	calib_cfg_cmd.ucd_calib_cfg.once.start = IWL_CALIB_INIT_CFG_ALL;
 93	calib_cfg_cmd.ucd_calib_cfg.once.send_res = IWL_CALIB_INIT_CFG_ALL;
 94	calib_cfg_cmd.ucd_calib_cfg.flags =
 95		IWL_CALIB_CFG_FLAG_SEND_COMPLETE_NTFY_MSK;
 96
 97	return iwl_dvm_send_cmd(priv, &cmd);
 98}
 99
100int iwl_init_alive_start(struct iwl_priv *priv)
101{
102	int ret;
103
104	if (priv->lib->bt_params &&
105	    priv->lib->bt_params->advanced_bt_coexist) {
106		/*
107		 * Tell uCode we are ready to perform calibration
108		 * need to perform this before any calibration
109		 * no need to close the envlope since we are going
110		 * to load the runtime uCode later.
111		 */
112		ret = iwl_send_bt_env(priv, IWL_BT_COEX_ENV_OPEN,
113			BT_COEX_PRIO_TBL_EVT_INIT_CALIB2);
114		if (ret)
115			return ret;
116
117	}
118
119	ret = iwl_send_calib_cfg(priv);
120	if (ret)
121		return ret;
122
123	/**
124	 * temperature offset calibration is only needed for runtime ucode,
125	 * so prepare the value now.
126	 */
127	if (priv->lib->need_temp_offset_calib) {
128		if (priv->lib->temp_offset_v2)
129			return iwl_set_temperature_offset_calib_v2(priv);
130		else
131			return iwl_set_temperature_offset_calib(priv);
132	}
133
134	return 0;
135}
136
137static int iwl_send_wimax_coex(struct iwl_priv *priv)
138{
139	struct iwl_wimax_coex_cmd coex_cmd;
140
141	/* coexistence is disabled */
142	memset(&coex_cmd, 0, sizeof(coex_cmd));
143
144	return iwl_dvm_send_cmd_pdu(priv,
145				COEX_PRIORITY_TABLE_CMD, 0,
146				sizeof(coex_cmd), &coex_cmd);
147}
148
149static const u8 iwl_bt_prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX] = {
150	((BT_COEX_PRIO_TBL_PRIO_BYPASS << IWL_BT_COEX_PRIO_TBL_PRIO_POS) |
151		(0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)),
152	((BT_COEX_PRIO_TBL_PRIO_BYPASS << IWL_BT_COEX_PRIO_TBL_PRIO_POS) |
153		(1 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)),
154	((BT_COEX_PRIO_TBL_PRIO_LOW << IWL_BT_COEX_PRIO_TBL_PRIO_POS) |
155		(0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)),
156	((BT_COEX_PRIO_TBL_PRIO_LOW << IWL_BT_COEX_PRIO_TBL_PRIO_POS) |
157		(1 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)),
158	((BT_COEX_PRIO_TBL_PRIO_HIGH << IWL_BT_COEX_PRIO_TBL_PRIO_POS) |
159		(0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)),
160	((BT_COEX_PRIO_TBL_PRIO_HIGH << IWL_BT_COEX_PRIO_TBL_PRIO_POS) |
161		(1 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)),
162	((BT_COEX_PRIO_TBL_PRIO_BYPASS << IWL_BT_COEX_PRIO_TBL_PRIO_POS) |
163		(0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)),
164	((BT_COEX_PRIO_TBL_PRIO_COEX_OFF << IWL_BT_COEX_PRIO_TBL_PRIO_POS) |
165		(0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)),
166	((BT_COEX_PRIO_TBL_PRIO_COEX_ON << IWL_BT_COEX_PRIO_TBL_PRIO_POS) |
167		(0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)),
168	0, 0, 0, 0, 0, 0, 0
169};
170
171void iwl_send_prio_tbl(struct iwl_priv *priv)
172{
173	struct iwl_bt_coex_prio_table_cmd prio_tbl_cmd;
174
175	memcpy(prio_tbl_cmd.prio_tbl, iwl_bt_prio_tbl,
176		sizeof(iwl_bt_prio_tbl));
177	if (iwl_dvm_send_cmd_pdu(priv,
178				REPLY_BT_COEX_PRIO_TABLE, 0,
179				sizeof(prio_tbl_cmd), &prio_tbl_cmd))
180		IWL_ERR(priv, "failed to send BT prio tbl command\n");
181}
182
183int iwl_send_bt_env(struct iwl_priv *priv, u8 action, u8 type)
184{
185	struct iwl_bt_coex_prot_env_cmd env_cmd;
186	int ret;
187
188	env_cmd.action = action;
189	env_cmd.type = type;
190	ret = iwl_dvm_send_cmd_pdu(priv,
191			       REPLY_BT_COEX_PROT_ENV, 0,
192			       sizeof(env_cmd), &env_cmd);
193	if (ret)
194		IWL_ERR(priv, "failed to send BT env command\n");
195	return ret;
196}
197
198static const u8 iwlagn_default_queue_to_tx_fifo[] = {
199	IWL_TX_FIFO_VO,
200	IWL_TX_FIFO_VI,
201	IWL_TX_FIFO_BE,
202	IWL_TX_FIFO_BK,
203};
204
205static const u8 iwlagn_ipan_queue_to_tx_fifo[] = {
206	IWL_TX_FIFO_VO,
207	IWL_TX_FIFO_VI,
208	IWL_TX_FIFO_BE,
209	IWL_TX_FIFO_BK,
210	IWL_TX_FIFO_BK_IPAN,
211	IWL_TX_FIFO_BE_IPAN,
212	IWL_TX_FIFO_VI_IPAN,
213	IWL_TX_FIFO_VO_IPAN,
214	IWL_TX_FIFO_BE_IPAN,
215	IWL_TX_FIFO_UNUSED,
216	IWL_TX_FIFO_AUX,
217};
218
219static int iwl_alive_notify(struct iwl_priv *priv)
220{
221	const u8 *queue_to_txf;
222	u8 n_queues;
223	int ret;
224	int i;
225
226	iwl_trans_fw_alive(priv->trans);
227
228	if (priv->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_PAN &&
229	    priv->nvm_data->sku_cap_ipan_enable) {
230		n_queues = ARRAY_SIZE(iwlagn_ipan_queue_to_tx_fifo);
231		queue_to_txf = iwlagn_ipan_queue_to_tx_fifo;
232	} else {
233		n_queues = ARRAY_SIZE(iwlagn_default_queue_to_tx_fifo);
234		queue_to_txf = iwlagn_default_queue_to_tx_fifo;
235	}
236
237	for (i = 0; i < n_queues; i++)
238		if (queue_to_txf[i] != IWL_TX_FIFO_UNUSED)
239			iwl_trans_ac_txq_enable(priv->trans, i,
240						queue_to_txf[i], 0);
241
242	priv->passive_no_rx = false;
243	priv->transport_queue_stop = 0;
244
245	ret = iwl_send_wimax_coex(priv);
246	if (ret)
247		return ret;
248
249	if (!priv->lib->no_xtal_calib) {
250		ret = iwl_set_Xtal_calib(priv);
251		if (ret)
252			return ret;
253	}
254
255	return iwl_send_calib_results(priv);
256}
257
258struct iwl_alive_data {
259	bool valid;
260	u8 subtype;
261};
262
263static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait,
264			 struct iwl_rx_packet *pkt, void *data)
265{
266	struct iwl_priv *priv =
267		container_of(notif_wait, struct iwl_priv, notif_wait);
268	struct iwl_alive_data *alive_data = data;
269	struct iwl_alive_resp *palive;
270
271	palive = (void *)pkt->data;
272
273	IWL_DEBUG_FW(priv, "Alive ucode status 0x%08X revision "
274		       "0x%01X 0x%01X\n",
275		       palive->is_valid, palive->ver_type,
276		       palive->ver_subtype);
277
278	priv->device_pointers.error_event_table =
279		le32_to_cpu(palive->error_event_table_ptr);
280	priv->device_pointers.log_event_table =
281		le32_to_cpu(palive->log_event_table_ptr);
282
283	alive_data->subtype = palive->ver_subtype;
284	alive_data->valid = palive->is_valid == UCODE_VALID_OK;
285
286	return true;
287}
288
289#define UCODE_ALIVE_TIMEOUT	HZ
290#define UCODE_CALIB_TIMEOUT	(2*HZ)
291
292int iwl_load_ucode_wait_alive(struct iwl_priv *priv,
293				 enum iwl_ucode_type ucode_type)
294{
295	struct iwl_notification_wait alive_wait;
296	struct iwl_alive_data alive_data;
297	int ret;
298	enum iwl_ucode_type old_type;
299	static const u16 alive_cmd[] = { REPLY_ALIVE };
300
301	old_type = priv->cur_ucode;
302	priv->cur_ucode = ucode_type;
303	priv->ucode_loaded = false;
304
305	iwl_init_notification_wait(&priv->notif_wait, &alive_wait,
306				   alive_cmd, ARRAY_SIZE(alive_cmd),
307				   iwl_alive_fn, &alive_data);
308
309	ret = iwl_trans_start_fw(priv->trans, priv->fw, ucode_type, false);
310	if (ret) {
311		priv->cur_ucode = old_type;
312		iwl_remove_notification(&priv->notif_wait, &alive_wait);
313		return ret;
314	}
315
316	/*
317	 * Some things may run in the background now, but we
318	 * just wait for the ALIVE notification here.
319	 */
320	ret = iwl_wait_notification(&priv->notif_wait, &alive_wait,
321					UCODE_ALIVE_TIMEOUT);
322	if (ret) {
323		priv->cur_ucode = old_type;
324		return ret;
325	}
326
327	if (!alive_data.valid) {
328		IWL_ERR(priv, "Loaded ucode is not valid!\n");
329		priv->cur_ucode = old_type;
330		return -EIO;
331	}
332
333	priv->ucode_loaded = true;
334
335	if (ucode_type != IWL_UCODE_WOWLAN) {
336		/* delay a bit to give rfkill time to run */
337		msleep(5);
338	}
339
340	ret = iwl_alive_notify(priv);
341	if (ret) {
342		IWL_WARN(priv,
343			"Could not complete ALIVE transition: %d\n", ret);
344		priv->cur_ucode = old_type;
345		return ret;
346	}
347
348	return 0;
349}
350
351static bool iwlagn_wait_calib(struct iwl_notif_wait_data *notif_wait,
352			      struct iwl_rx_packet *pkt, void *data)
353{
354	struct iwl_priv *priv = data;
355	struct iwl_calib_cmd *cmd;
356
357	if (pkt->hdr.cmd != CALIBRATION_RES_NOTIFICATION) {
358		WARN_ON(pkt->hdr.cmd != CALIBRATION_COMPLETE_NOTIFICATION);
359		return true;
360	}
361
362	cmd = (struct iwl_calib_cmd *)pkt->data;
363
364	if (iwl_calib_set(priv, cmd, iwl_rx_packet_payload_len(pkt)))
365		IWL_ERR(priv, "Failed to record calibration data %d\n",
366			cmd->hdr.op_code);
367
368	return false;
369}
370
371int iwl_run_init_ucode(struct iwl_priv *priv)
372{
373	struct iwl_notification_wait calib_wait;
374	static const u16 calib_complete[] = {
375		CALIBRATION_RES_NOTIFICATION,
376		CALIBRATION_COMPLETE_NOTIFICATION
377	};
378	int ret;
379
380	lockdep_assert_held(&priv->mutex);
381
382	/* No init ucode required? Curious, but maybe ok */
383	if (!priv->fw->img[IWL_UCODE_INIT].num_sec)
384		return 0;
385
386	iwl_init_notification_wait(&priv->notif_wait, &calib_wait,
387				   calib_complete, ARRAY_SIZE(calib_complete),
388				   iwlagn_wait_calib, priv);
389
390	/* Will also start the device */
391	ret = iwl_load_ucode_wait_alive(priv, IWL_UCODE_INIT);
392	if (ret)
393		goto error;
394
395	ret = iwl_init_alive_start(priv);
396	if (ret)
397		goto error;
398
399	/*
400	 * Some things may run in the background now, but we
401	 * just wait for the calibration complete notification.
402	 */
403	ret = iwl_wait_notification(&priv->notif_wait, &calib_wait,
404					UCODE_CALIB_TIMEOUT);
405
406	goto out;
407
408 error:
409	iwl_remove_notification(&priv->notif_wait, &calib_wait);
410 out:
411	/* Whatever happened, stop the device */
412	iwl_trans_stop_device(priv->trans);
413	priv->ucode_loaded = false;
414
415	return ret;
416}
~/ Read-only file preview.