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