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 */ 30 static 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 41 static 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 56 static 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 81 static 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 100 int 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 137 static 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 149 static 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 171 void 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 183 int 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 198 static 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 205 static 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 219 static 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 258 struct iwl_alive_data { 259 bool valid; 260 u8 subtype; 261 }; 262 263 static 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 292 int 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 351 static 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 371 int 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 } 417