| /****************************************************************************** |
| * |
| * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. |
| * Copyright(c) 2015 - 2017 Intel Deutschland GmbH |
| * Copyright (C) 2018 Intel Corporation |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in |
| * the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name Intel Corporation nor the names of its |
| * contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| * |
| *****************************************************************************/ |
| #include "fw/dbg.h" |
| #include "fw/img.h" |
| #include "fw/testmode.h" |
| #include "iwl-csr.h" |
| #include "iwl-dnt-cfg.h" |
| #include "iwl-op-mode.h" |
| #include "iwl-trans.h" |
| #include "xvt.h" |
| |
| #define XVT_UCODE_ALIVE_TIMEOUT (HZ * CPTCFG_IWL_TIMEOUT_FACTOR) |
| |
| struct iwl_xvt_alive_data { |
| bool valid; |
| uint32_t scd_base_addr; |
| }; |
| |
| static int iwl_xvt_send_dqa_cmd(struct iwl_xvt* xvt) { |
| struct iwl_dqa_enable_cmd dqa_cmd = { |
| .cmd_queue = cpu_to_le32(IWL_MVM_DQA_CMD_QUEUE), |
| }; |
| uint32_t cmd_id = iwl_cmd_id(DQA_ENABLE_CMD, DATA_PATH_GROUP, 0); |
| int ret; |
| |
| ret = iwl_xvt_send_cmd_pdu(xvt, cmd_id, 0, sizeof(dqa_cmd), &dqa_cmd); |
| if (ret) { |
| IWL_ERR(xvt, "Failed to send DQA enabling command: %d\n", ret); |
| } else { |
| IWL_DEBUG_FW(xvt, "Working in DQA mode\n"); |
| } |
| |
| return ret; |
| } |
| |
| static bool iwl_alive_fn(struct iwl_notif_wait_data* notif_wait, struct iwl_rx_packet* pkt, |
| void* data) { |
| struct iwl_xvt* xvt = container_of(notif_wait, struct iwl_xvt, notif_wait); |
| struct iwl_xvt_alive_data* alive_data = data; |
| struct xvt_alive_resp_ver2* palive2; |
| struct mvm_alive_resp_v3* palive3; |
| struct mvm_alive_resp* palive4; |
| struct iwl_lmac_alive *lmac1, *lmac2; |
| struct iwl_umac_alive* umac; |
| uint32_t rx_packet_payload_size = iwl_rx_packet_payload_len(pkt); |
| uint16_t status, flags; |
| xvt->support_umac_log = false; |
| |
| if (rx_packet_payload_size == sizeof(*palive2)) { |
| palive2 = (void*)pkt->data; |
| |
| xvt->error_event_table[0] = le32_to_cpu(palive2->error_event_table_ptr); |
| alive_data->scd_base_addr = le32_to_cpu(palive2->scd_base_ptr); |
| |
| alive_data->valid = le16_to_cpu(palive2->status) == IWL_ALIVE_STATUS_OK; |
| iwl_tm_set_fw_ver(xvt->trans, palive2->ucode_major, palive2->ucode_minor); |
| xvt->umac_error_event_table = le32_to_cpu(palive2->error_info_addr); |
| if (xvt->umac_error_event_table) { |
| xvt->support_umac_log = true; |
| } |
| |
| IWL_DEBUG_FW(xvt, |
| "Alive VER2 ucode status 0x%04x revision 0x%01X " |
| "0x%01X flags 0x%01X\n", |
| le16_to_cpu(palive2->status), palive2->ver_type, palive2->ver_subtype, |
| palive2->flags); |
| |
| IWL_DEBUG_FW(xvt, "UMAC version: Major - 0x%x, Minor - 0x%x\n", palive2->umac_major, |
| palive2->umac_minor); |
| } else { |
| if (rx_packet_payload_size == sizeof(*palive3)) { |
| palive3 = (void*)pkt->data; |
| status = le16_to_cpu(palive3->status); |
| flags = le16_to_cpu(palive3->flags); |
| lmac1 = &palive3->lmac_data; |
| umac = &palive3->umac_data; |
| |
| IWL_DEBUG_FW(xvt, "Alive VER3\n"); |
| } else if (rx_packet_payload_size == sizeof(*palive4)) { |
| palive4 = (void*)pkt->data; |
| status = le16_to_cpu(palive4->status); |
| flags = le16_to_cpu(palive4->flags); |
| lmac1 = &palive4->lmac_data[0]; |
| lmac2 = &palive4->lmac_data[1]; |
| umac = &palive4->umac_data; |
| xvt->error_event_table[1] = le32_to_cpu(lmac2->error_event_table_ptr); |
| |
| IWL_DEBUG_FW(xvt, "Alive VER4 CDB\n"); |
| } else { |
| IWL_ERR(xvt, "unrecognized alive notificatio\n"); |
| return false; |
| } |
| |
| alive_data->valid = status == IWL_ALIVE_STATUS_OK; |
| xvt->error_event_table[0] = le32_to_cpu(lmac1->error_event_table_ptr); |
| alive_data->scd_base_addr = le32_to_cpu(lmac1->scd_base_ptr); |
| iwl_tm_set_fw_ver(xvt->trans, le32_to_cpu(lmac1->ucode_major), le32_to_cpu(lmac1->ucode_minor)); |
| xvt->umac_error_event_table = le32_to_cpu(umac->error_info_addr); |
| if (xvt->umac_error_event_table) { |
| xvt->support_umac_log = true; |
| } |
| |
| IWL_DEBUG_FW(xvt, "status 0x%04x rev 0x%01X 0x%01X flags 0x%01X\n", status, lmac1->ver_type, |
| lmac1->ver_subtype, flags); |
| IWL_DEBUG_FW(xvt, "UMAC version: Major - 0x%x, Minor - 0x%x\n", umac->umac_major, |
| umac->umac_minor); |
| } |
| |
| return true; |
| } |
| |
| static int iwl_xvt_load_ucode_wait_alive(struct iwl_xvt* xvt, enum iwl_ucode_type ucode_type) { |
| struct iwl_notification_wait alive_wait; |
| struct iwl_xvt_alive_data alive_data; |
| const struct fw_img* fw; |
| int ret; |
| enum iwl_ucode_type old_type = xvt->fwrt.cur_fw_img; |
| static const uint16_t alive_cmd[] = {MVM_ALIVE}; |
| struct iwl_scd_txq_cfg_cmd cmd = { |
| .scd_queue = IWL_XVT_DEFAULT_TX_QUEUE, |
| .action = SCD_CFG_ENABLE_QUEUE, |
| .window = IWL_FRAME_LIMIT, |
| .sta_id = IWL_XVT_TX_STA_ID_DEFAULT, |
| .ssn = 0, |
| .tx_fifo = IWL_XVT_DEFAULT_TX_FIFO, |
| .aggregate = false, |
| .tid = IWL_MAX_TID_COUNT, |
| }; |
| |
| iwl_fw_set_current_image(&xvt->fwrt, ucode_type); |
| fw = iwl_get_ucode_image(xvt->fw, ucode_type); |
| |
| if (!fw) { |
| return -EINVAL; |
| } |
| |
| iwl_init_notification_wait(&xvt->notif_wait, &alive_wait, alive_cmd, ARRAY_SIZE(alive_cmd), |
| iwl_alive_fn, &alive_data); |
| |
| ret = |
| iwl_trans_start_fw_dbg(xvt->trans, fw, ucode_type == IWL_UCODE_INIT, |
| (xvt->sw_stack_cfg.fw_dbg_flags & ~IWL_XVT_DBG_FLAGS_NO_DEFAULT_TXQ)); |
| if (ret) { |
| iwl_fw_set_current_image(&xvt->fwrt, old_type); |
| iwl_remove_notification(&xvt->notif_wait, &alive_wait); |
| return ret; |
| } |
| |
| /* |
| * Some things may run in the background now, but we |
| * just wait for the ALIVE notification here. |
| */ |
| ret = iwl_wait_notification(&xvt->notif_wait, &alive_wait, XVT_UCODE_ALIVE_TIMEOUT); |
| if (ret) { |
| iwl_fw_set_current_image(&xvt->fwrt, old_type); |
| return ret; |
| } |
| |
| if (!alive_data.valid) { |
| IWL_ERR(xvt, "Loaded ucode is not valid!\n"); |
| iwl_fw_set_current_image(&xvt->fwrt, old_type); |
| return -EIO; |
| } |
| |
| /* fresh firmware was loaded */ |
| xvt->fw_error = false; |
| |
| iwl_trans_fw_alive(xvt->trans, alive_data.scd_base_addr); |
| |
| ret = iwl_init_paging(&xvt->fwrt, ucode_type); |
| if (ret) { |
| return ret; |
| } |
| |
| if (ucode_type == IWL_UCODE_REGULAR) { |
| ret = iwl_xvt_send_dqa_cmd(xvt); |
| if (ret) { |
| return ret; |
| } |
| } |
| /* |
| * Starting from 22000 tx queue allocation must be done after add |
| * station, so it is not part of the init flow. |
| */ |
| if (!iwl_xvt_is_unified_fw(xvt) && iwl_xvt_has_default_txq(xvt)) { |
| iwl_trans_txq_enable_cfg(xvt->trans, IWL_XVT_DEFAULT_TX_QUEUE, 0, NULL, 0); |
| |
| WARN(iwl_xvt_send_cmd_pdu(xvt, SCD_QUEUE_CFG, 0, sizeof(cmd), &cmd), |
| "Failed to configure queue %d on FIFO %d\n", IWL_XVT_DEFAULT_TX_QUEUE, |
| IWL_XVT_DEFAULT_TX_FIFO); |
| xvt->tx_meta_data[XVT_LMAC_0_ID].queue = IWL_XVT_DEFAULT_TX_QUEUE; |
| } |
| |
| xvt->fw_running = true; |
| |
| return 0; |
| } |
| |
| static int iwl_xvt_send_extended_config(struct iwl_xvt* xvt) { |
| /* |
| * TODO: once WRT will be implemented in xVT, IWL_INIT_DEBUG_CFG |
| * flag will not always be set |
| */ |
| struct iwl_init_extended_cfg_cmd ext_cfg = { |
| .init_flags = cpu_to_le32(BIT(IWL_INIT_NVM) | BIT(IWL_INIT_DEBUG_CFG)), |
| |
| }; |
| |
| if (xvt->sw_stack_cfg.load_mask & IWL_XVT_LOAD_MASK_RUNTIME) { |
| ext_cfg.init_flags |= cpu_to_le32(BIT(IWL_INIT_PHY)); |
| } |
| |
| return iwl_xvt_send_cmd_pdu(xvt, WIDE_ID(SYSTEM_GROUP, INIT_EXTENDED_CFG_CMD), 0, sizeof(ext_cfg), |
| &ext_cfg); |
| } |
| |
| int iwl_xvt_run_fw(struct iwl_xvt* xvt, uint32_t ucode_type, bool cont_run) { |
| int ret; |
| |
| if (ucode_type >= IWL_UCODE_TYPE_MAX) { |
| return -EINVAL; |
| } |
| |
| iwl_assert_lock_held(&xvt->mutex); |
| |
| if (xvt->state != IWL_XVT_STATE_UNINITIALIZED) { |
| if (xvt->fw_running) { |
| xvt->fw_running = false; |
| if (xvt->fwrt.cur_fw_img == IWL_UCODE_REGULAR) { |
| iwl_xvt_txq_disable(xvt); |
| } |
| } |
| _iwl_trans_stop_device(xvt->trans, !cont_run); |
| } |
| |
| if (cont_run) { |
| ret = _iwl_trans_start_hw(xvt->trans, false); |
| } else { |
| ret = iwl_trans_start_hw(xvt->trans); |
| } |
| if (ret) { |
| IWL_ERR(xvt, "Failed to start HW\n"); |
| return ret; |
| } |
| |
| iwl_trans_set_bits_mask(xvt->trans, CSR_HW_IF_CONFIG_REG, CSR_HW_IF_CONFIG_REG_BIT_MAC_SI, |
| CSR_HW_IF_CONFIG_REG_BIT_MAC_SI); |
| |
| /* Will also start the device */ |
| ret = iwl_xvt_load_ucode_wait_alive(xvt, ucode_type); |
| if (ret) { |
| IWL_ERR(xvt, "Failed to start ucode: %d\n", ret); |
| iwl_trans_stop_device(xvt->trans); |
| } |
| |
| if (iwl_xvt_is_unified_fw(xvt)) { |
| ret = iwl_xvt_send_extended_config(xvt); |
| if (ret) { |
| IWL_ERR(xvt, "Failed to send extended_config: %d\n", ret); |
| iwl_trans_stop_device(xvt->trans); |
| return ret; |
| } |
| } |
| iwl_dnt_start(xvt->trans); |
| |
| xvt->fwrt.dump.conf = FW_DBG_INVALID; |
| /* if we have a destination, assume EARLY START */ |
| if (xvt->fw->dbg.dest_tlv) { |
| xvt->fwrt.dump.conf = FW_DBG_START_FROM_ALIVE; |
| } |
| iwl_fw_start_dbg_conf(&xvt->fwrt, FW_DBG_START_FROM_ALIVE); |
| |
| return ret; |
| } |