/* * toggle the bit to wake up uCode and check the temperature * if the temperature is below CT, uCode will stay awake and send card * state notification with CT_KILL bit clear to inform Thermal Throttling * Management to change state. Otherwise, uCode will go back to sleep * without doing anything, driver should continue the 5 seconds timer * to wake up uCode for temperature check until temperature drop below CT */ static void iwl_tt_check_exit_ct_kill(unsigned long data) { struct iwl_priv *priv = (struct iwl_priv *)data; struct iwl_tt_mgmt *tt = &priv->thermal_throttle; unsigned long flags; if (test_bit(STATUS_EXIT_PENDING, &priv->status)) return; if (tt->state == IWL_TI_CT_KILL) { if (priv->thermal_throttle.ct_kill_toggle) { iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT); priv->thermal_throttle.ct_kill_toggle = false; } else { iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_SET, CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT); priv->thermal_throttle.ct_kill_toggle = true; } iwl_read32(priv->trans, CSR_UCODE_DRV_GP1); if (iwl_trans_grab_nic_access(priv->trans, false, &flags)) iwl_trans_release_nic_access(priv->trans, &flags); /* Reschedule the ct_kill timer to occur in * CT_KILL_EXIT_DURATION seconds to ensure we get a * thermal update */ IWL_DEBUG_TEMP(priv, "schedule ct_kill exit timer\n"); mod_timer(&priv->thermal_throttle.ct_kill_exit_tm, jiffies + CT_KILL_EXIT_DURATION * HZ); } }
static void iwl_mvm_read_radio_reg(struct iwl_mvm *mvm, struct iwl_fw_error_dump_data **dump_data) { u8 *pos = (void *)(*dump_data)->data; unsigned long flags; int i; if (!iwl_trans_grab_nic_access(mvm->trans, &flags)) return; (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RADIO_REG); (*dump_data)->len = cpu_to_le32(RADIO_REG_MAX_READ); for (i = 0; i < RADIO_REG_MAX_READ; i++) { u32 rd_cmd = RADIO_RSP_RD_CMD; rd_cmd |= i << RADIO_RSP_ADDR_POS; iwl_write_prph_no_grab(mvm->trans, RSP_RADIO_CMD, rd_cmd); *pos = (u8)iwl_read_prph_no_grab(mvm->trans, RSP_RADIO_RDDAT); pos++; } *dump_data = iwl_fw_error_next_data(*dump_data); iwl_trans_release_nic_access(mvm->trans, &flags); }
void iwl_write_direct32(struct iwl_trans *trans, u32 reg, u32 value) { unsigned long flags; if (iwl_trans_grab_nic_access(trans, false, &flags)) { iwl_write32(trans, reg, value); iwl_trans_release_nic_access(trans, &flags); } }
void iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val) { unsigned long flags; if (iwl_trans_grab_nic_access(trans, false, &flags)) { __iwl_write_prph(trans, ofs, val); iwl_trans_release_nic_access(trans, &flags); } }
void iwl_write_direct64(struct iwl_trans *trans, u64 reg, u64 value) { unsigned long flags; if (iwl_trans_grab_nic_access(trans, &flags)) { iwl_write64(trans, reg, value); iwl_trans_release_nic_access(trans, &flags); } }
void iwl_set_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask) { unsigned long flags; if (iwl_trans_grab_nic_access(trans, false, &flags)) { __iwl_write_prph(trans, ofs, __iwl_read_prph(trans, ofs) | mask); iwl_trans_release_nic_access(trans, &flags); } }
u32 iwl_read_direct32(struct iwl_trans *trans, u32 reg) { u32 value = 0x5a5a5a5a; unsigned long flags; if (iwl_trans_grab_nic_access(trans, false, &flags)) { value = iwl_read32(trans, reg); iwl_trans_release_nic_access(trans, &flags); } return value; }
void iwl_clear_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask) { unsigned long flags; u32 val; if (iwl_trans_grab_nic_access(trans, false, &flags)) { val = __iwl_read_prph(trans, ofs); __iwl_write_prph(trans, ofs, (val & ~mask)); iwl_trans_release_nic_access(trans, &flags); } }
u32 iwl_read_prph(struct iwl_trans *trans, u32 ofs) { unsigned long flags; u32 val = 0x5a5a5a5a; if (iwl_trans_grab_nic_access(trans, false, &flags)) { val = __iwl_read_prph(trans, ofs); iwl_trans_release_nic_access(trans, &flags); } return val; }
void iwl_set_bits_mask_prph(struct iwl_trans *trans, u32 ofs, u32 bits, u32 mask) { unsigned long flags; if (iwl_trans_grab_nic_access(trans, &flags)) { iwl_write_prph_no_grab(trans, ofs, (iwl_read_prph_no_grab(trans, ofs) & mask) | bits); iwl_trans_release_nic_access(trans, &flags); } }
static u32 iwl_dump_prph(struct iwl_trans *trans, struct iwl_fw_error_dump_data **data) { struct iwl_fw_error_dump_prph *prph; unsigned long flags; u32 prph_len = 0, i; if (!iwl_trans_grab_nic_access(trans, &flags)) return 0; for (i = 0; i < ARRAY_SIZE(iwl_prph_dump_addr); i++) { /* The range includes both boundaries */ int num_bytes_in_chunk = iwl_prph_dump_addr[i].end - iwl_prph_dump_addr[i].start + 4; int reg; __le32 *val; prph_len += sizeof(**data) + sizeof(*prph) + num_bytes_in_chunk; (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PRPH); (*data)->len = cpu_to_le32(sizeof(*prph) + num_bytes_in_chunk); prph = (void *)(*data)->data; prph->prph_start = cpu_to_le32(iwl_prph_dump_addr[i].start); val = (void *)prph->data; for (reg = iwl_prph_dump_addr[i].start; reg <= iwl_prph_dump_addr[i].end; reg += 4) *val++ = cpu_to_le32(iwl_read_prph_no_grab(trans, reg)); *data = iwl_fw_error_next_data(*data); } iwl_trans_release_nic_access(trans, &flags); return prph_len; }
static void iwl_mvm_dump_fifos(struct iwl_mvm *mvm, struct iwl_fw_error_dump_data **dump_data) { struct iwl_fw_error_dump_fifo *fifo_hdr; u32 *fifo_data; u32 fifo_len; unsigned long flags; int i, j; if (!iwl_trans_grab_nic_access(mvm->trans, &flags)) return; /* Pull RXF data from all RXFs */ for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.rxfifo_size); i++) { /* * Keep aside the additional offset that might be needed for * next RXF */ u32 offset_diff = RXF_DIFF_FROM_PREV * i; fifo_hdr = (void *)(*dump_data)->data; fifo_data = (void *)fifo_hdr->data; fifo_len = mvm->shared_mem_cfg.rxfifo_size[i]; /* No need to try to read the data if the length is 0 */ if (fifo_len == 0) continue; /* Add a TLV for the RXF */ (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RXF); (*dump_data)->len = cpu_to_le32(fifo_len + sizeof(*fifo_hdr)); fifo_hdr->fifo_num = cpu_to_le32(i); fifo_hdr->available_bytes = cpu_to_le32(iwl_trans_read_prph(mvm->trans, RXF_RD_D_SPACE + offset_diff)); fifo_hdr->wr_ptr = cpu_to_le32(iwl_trans_read_prph(mvm->trans, RXF_RD_WR_PTR + offset_diff)); fifo_hdr->rd_ptr = cpu_to_le32(iwl_trans_read_prph(mvm->trans, RXF_RD_RD_PTR + offset_diff)); fifo_hdr->fence_ptr = cpu_to_le32(iwl_trans_read_prph(mvm->trans, RXF_RD_FENCE_PTR + offset_diff)); fifo_hdr->fence_mode = cpu_to_le32(iwl_trans_read_prph(mvm->trans, RXF_SET_FENCE_MODE + offset_diff)); /* Lock fence */ iwl_trans_write_prph(mvm->trans, RXF_SET_FENCE_MODE + offset_diff, 0x1); /* Set fence pointer to the same place like WR pointer */ iwl_trans_write_prph(mvm->trans, RXF_LD_WR2FENCE + offset_diff, 0x1); /* Set fence offset */ iwl_trans_write_prph(mvm->trans, RXF_LD_FENCE_OFFSET_ADDR + offset_diff, 0x0); /* Read FIFO */ fifo_len /= sizeof(u32); /* Size in DWORDS */ for (j = 0; j < fifo_len; j++) fifo_data[j] = iwl_trans_read_prph(mvm->trans, RXF_FIFO_RD_FENCE_INC + offset_diff); *dump_data = iwl_fw_error_next_data(*dump_data); } /* Pull TXF data from all TXFs */ for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.txfifo_size); i++) { /* Mark the number of TXF we're pulling now */ iwl_trans_write_prph(mvm->trans, TXF_LARC_NUM, i); fifo_hdr = (void *)(*dump_data)->data; fifo_data = (void *)fifo_hdr->data; fifo_len = mvm->shared_mem_cfg.txfifo_size[i]; /* No need to try to read the data if the length is 0 */ if (fifo_len == 0) continue; /* Add a TLV for the FIFO */ (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_TXF); (*dump_data)->len = cpu_to_le32(fifo_len + sizeof(*fifo_hdr)); fifo_hdr->fifo_num = cpu_to_le32(i); fifo_hdr->available_bytes = cpu_to_le32(iwl_trans_read_prph(mvm->trans, TXF_FIFO_ITEM_CNT)); fifo_hdr->wr_ptr = cpu_to_le32(iwl_trans_read_prph(mvm->trans, TXF_WR_PTR)); fifo_hdr->rd_ptr = cpu_to_le32(iwl_trans_read_prph(mvm->trans, TXF_RD_PTR)); fifo_hdr->fence_ptr = cpu_to_le32(iwl_trans_read_prph(mvm->trans, TXF_FENCE_PTR)); fifo_hdr->fence_mode = cpu_to_le32(iwl_trans_read_prph(mvm->trans, TXF_LOCK_FENCE)); /* Set the TXF_READ_MODIFY_ADDR to TXF_WR_PTR */ iwl_trans_write_prph(mvm->trans, TXF_READ_MODIFY_ADDR, TXF_WR_PTR); /* Dummy-read to advance the read pointer to the head */ iwl_trans_read_prph(mvm->trans, TXF_READ_MODIFY_DATA); /* Read FIFO */ fifo_len /= sizeof(u32); /* Size in DWORDS */ for (j = 0; j < fifo_len; j++) fifo_data[j] = iwl_trans_read_prph(mvm->trans, TXF_READ_MODIFY_DATA); *dump_data = iwl_fw_error_next_data(*dump_data); } if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG)) { /* Pull UMAC internal TXF data from all TXFs */ for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.internal_txfifo_size); i++) { /* Mark the number of TXF we're pulling now */ iwl_trans_write_prph(mvm->trans, TXF_CPU2_NUM, i); fifo_hdr = (void *)(*dump_data)->data; fifo_data = (void *)fifo_hdr->data; fifo_len = mvm->shared_mem_cfg.internal_txfifo_size[i]; /* No need to try to read the data if the length is 0 */ if (fifo_len == 0) continue; /* Add a TLV for the internal FIFOs */ (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_INTERNAL_TXF); (*dump_data)->len = cpu_to_le32(fifo_len + sizeof(*fifo_hdr)); fifo_hdr->fifo_num = cpu_to_le32(i); fifo_hdr->available_bytes = cpu_to_le32(iwl_trans_read_prph(mvm->trans, TXF_CPU2_FIFO_ITEM_CNT)); fifo_hdr->wr_ptr = cpu_to_le32(iwl_trans_read_prph(mvm->trans, TXF_CPU2_WR_PTR)); fifo_hdr->rd_ptr = cpu_to_le32(iwl_trans_read_prph(mvm->trans, TXF_CPU2_RD_PTR)); fifo_hdr->fence_ptr = cpu_to_le32(iwl_trans_read_prph(mvm->trans, TXF_CPU2_FENCE_PTR)); fifo_hdr->fence_mode = cpu_to_le32(iwl_trans_read_prph(mvm->trans, TXF_CPU2_LOCK_FENCE)); /* Set TXF_CPU2_READ_MODIFY_ADDR to TXF_CPU2_WR_PTR */ iwl_trans_write_prph(mvm->trans, TXF_CPU2_READ_MODIFY_ADDR, TXF_CPU2_WR_PTR); /* Dummy-read to advance the read pointer to head */ iwl_trans_read_prph(mvm->trans, TXF_CPU2_READ_MODIFY_DATA); /* Read FIFO */ fifo_len /= sizeof(u32); /* Size in DWORDS */ for (j = 0; j < fifo_len; j++) fifo_data[j] = iwl_trans_read_prph(mvm->trans, TXF_CPU2_READ_MODIFY_DATA); *dump_data = iwl_fw_error_next_data(*dump_data); } } iwl_trans_release_nic_access(mvm->trans, &flags); }
static void iwl_mvm_dump_fifos(struct iwl_mvm *mvm, struct iwl_fw_error_dump_data **dump_data) { struct iwl_fw_error_dump_fifo *fifo_hdr; struct iwl_mvm_shared_mem_cfg *cfg = &mvm->smem_cfg; u32 *fifo_data; u32 fifo_len; unsigned long flags; int i, j; if (!iwl_trans_grab_nic_access(mvm->trans, &flags)) return; /* Pull RXF1 */ iwl_mvm_dump_rxf(mvm, dump_data, cfg->lmac[0].rxfifo1_size, 0, 0); /* Pull RXF2 */ iwl_mvm_dump_rxf(mvm, dump_data, cfg->rxfifo2_size, RXF_DIFF_FROM_PREV, 1); /* Pull LMAC2 RXF1 */ if (mvm->smem_cfg.num_lmacs > 1) iwl_mvm_dump_rxf(mvm, dump_data, cfg->lmac[1].rxfifo1_size, LMAC2_PRPH_OFFSET, 2); /* Pull TXF data from LMAC1 */ for (i = 0; i < mvm->smem_cfg.num_txfifo_entries; i++) { /* Mark the number of TXF we're pulling now */ iwl_trans_write_prph(mvm->trans, TXF_LARC_NUM, i); iwl_mvm_dump_txf(mvm, dump_data, cfg->lmac[0].txfifo_size[i], 0, i); } /* Pull TXF data from LMAC2 */ if (mvm->smem_cfg.num_lmacs > 1) { for (i = 0; i < mvm->smem_cfg.num_txfifo_entries; i++) { /* Mark the number of TXF we're pulling now */ iwl_trans_write_prph(mvm->trans, TXF_LARC_NUM + LMAC2_PRPH_OFFSET, i); iwl_mvm_dump_txf(mvm, dump_data, cfg->lmac[1].txfifo_size[i], LMAC2_PRPH_OFFSET, i + cfg->num_txfifo_entries); } } if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG)) { /* Pull UMAC internal TXF data from all TXFs */ for (i = 0; i < ARRAY_SIZE(mvm->smem_cfg.internal_txfifo_size); i++) { fifo_hdr = (void *)(*dump_data)->data; fifo_data = (void *)fifo_hdr->data; fifo_len = mvm->smem_cfg.internal_txfifo_size[i]; /* No need to try to read the data if the length is 0 */ if (fifo_len == 0) continue; /* Add a TLV for the internal FIFOs */ (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_INTERNAL_TXF); (*dump_data)->len = cpu_to_le32(fifo_len + sizeof(*fifo_hdr)); fifo_hdr->fifo_num = cpu_to_le32(i); /* Mark the number of TXF we're pulling now */ iwl_trans_write_prph(mvm->trans, TXF_CPU2_NUM, i + mvm->smem_cfg.num_txfifo_entries); fifo_hdr->available_bytes = cpu_to_le32(iwl_trans_read_prph(mvm->trans, TXF_CPU2_FIFO_ITEM_CNT)); fifo_hdr->wr_ptr = cpu_to_le32(iwl_trans_read_prph(mvm->trans, TXF_CPU2_WR_PTR)); fifo_hdr->rd_ptr = cpu_to_le32(iwl_trans_read_prph(mvm->trans, TXF_CPU2_RD_PTR)); fifo_hdr->fence_ptr = cpu_to_le32(iwl_trans_read_prph(mvm->trans, TXF_CPU2_FENCE_PTR)); fifo_hdr->fence_mode = cpu_to_le32(iwl_trans_read_prph(mvm->trans, TXF_CPU2_LOCK_FENCE)); /* Set TXF_CPU2_READ_MODIFY_ADDR to TXF_CPU2_WR_PTR */ iwl_trans_write_prph(mvm->trans, TXF_CPU2_READ_MODIFY_ADDR, TXF_CPU2_WR_PTR); /* Dummy-read to advance the read pointer to head */ iwl_trans_read_prph(mvm->trans, TXF_CPU2_READ_MODIFY_DATA); /* Read FIFO */ fifo_len /= sizeof(u32); /* Size in DWORDS */ for (j = 0; j < fifo_len; j++) fifo_data[j] = iwl_trans_read_prph(mvm->trans, TXF_CPU2_READ_MODIFY_DATA); *dump_data = iwl_fw_error_next_data(*dump_data); } } iwl_trans_release_nic_access(mvm->trans, &flags); }
static int iwl_mvm_tm_send_hcmd(struct iwl_mvm *mvm, struct iwl_tm_data *data_in, struct iwl_tm_data *data_out) { struct iwl_tm_cmd_request *hcmd_req = data_in->data; struct iwl_tm_cmd_request *cmd_resp; u32 reply_len, resp_size; struct iwl_rx_packet *pkt; struct iwl_host_cmd host_cmd = { .id = hcmd_req->id, .data[0] = hcmd_req->data, .len[0] = hcmd_req->len, .dataflags[0] = IWL_HCMD_DFL_NOCOPY, .flags = CMD_SYNC, }; int ret; if (hcmd_req->want_resp) host_cmd.flags |= CMD_WANT_SKB; mutex_lock(&mvm->mutex); ret = iwl_mvm_send_cmd(mvm, &host_cmd); mutex_unlock(&mvm->mutex); if (ret) return ret; /* if no reply is required, we are done */ if (!(host_cmd.flags & CMD_WANT_SKB)) return 0; /* Retrieve response packet */ pkt = host_cmd.resp_pkt; if (!pkt) { IWL_ERR(mvm->trans, "HCMD received a null response packet\n"); return -ENOMSG; } reply_len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; /* Set response data */ resp_size = sizeof(struct iwl_tm_cmd_request) + reply_len; cmd_resp = kzalloc(resp_size, GFP_KERNEL); if (!cmd_resp) { iwl_free_resp(&host_cmd); return -ENOMEM; } cmd_resp->id = hcmd_req->id; cmd_resp->len = reply_len; memcpy(cmd_resp->data, &(pkt->hdr), reply_len); iwl_free_resp(&host_cmd); data_out->data = cmd_resp; data_out->len = resp_size; return 0; } static void iwl_mvm_tm_execute_reg_ops(struct iwl_trans *trans, struct iwl_tm_regs_request *request, struct iwl_tm_regs_request *result) { struct iwl_tm_reg_op *cur_op; u32 idx, read_idx; for (idx = 0, read_idx = 0; idx < request->num; idx++) { cur_op = &request->reg_ops[idx]; if (cur_op->op_type == IWL_TM_REG_OP_READ) { cur_op->value = iwl_read32(trans, cur_op->address); memcpy(&result->reg_ops[read_idx], cur_op, sizeof(*cur_op)); read_idx++; } else { /* IWL_TM_REG_OP_WRITE is the only possible option */ iwl_write32(trans, cur_op->address, cur_op->value); } } } static int iwl_mvm_tm_reg_ops(struct iwl_trans *trans, struct iwl_tm_data *data_in, struct iwl_tm_data *data_out) { struct iwl_tm_reg_op *cur_op; struct iwl_tm_regs_request *request = data_in->data; struct iwl_tm_regs_request *result; u32 result_size; u32 idx, read_idx; bool is_grab_nic_access_required = true; unsigned long flags; /* Calculate result size (result is returned only for read ops) */ for (idx = 0, read_idx = 0; idx < request->num; idx++) { if (request->reg_ops[idx].op_type == IWL_TM_REG_OP_READ) read_idx++; /* check if there is an operation that it is not */ /* in the CSR range (0x00000000 - 0x000003FF) */ /* and not in the AL range */ cur_op = &request->reg_ops[idx]; if (IS_AL_ADDR(cur_op->address) || (cur_op->address < HBUS_BASE)) is_grab_nic_access_required = false; } result_size = sizeof(struct iwl_tm_regs_request) + read_idx*sizeof(struct iwl_tm_reg_op); result = kzalloc(result_size, GFP_KERNEL); if (!result) return -ENOMEM; result->num = read_idx; if (is_grab_nic_access_required) { if (!iwl_trans_grab_nic_access(trans, false, &flags)) { kfree(result); return -EBUSY; } iwl_mvm_tm_execute_reg_ops(trans, request, result); iwl_trans_release_nic_access(trans, &flags); } else { iwl_mvm_tm_execute_reg_ops(trans, request, result); } data_out->data = result; data_out->len = result_size; return 0; } static int iwl_tm_get_dev_info(struct iwl_mvm *mvm, struct iwl_tm_data *data_out) { struct iwl_tm_dev_info *dev_info; const u8 driver_ver[] = IWLWIFI_VERSION; if (!mvm->nvm_data) return -EINVAL; dev_info = kzalloc(sizeof(struct iwl_tm_dev_info) + (strlen(driver_ver)+1)*sizeof(u8), GFP_KERNEL); if (!dev_info) return -ENOMEM; dev_info->dev_id = mvm->trans->hw_id; dev_info->fw_ver = mvm->fw->ucode_ver; dev_info->vendor_id = PCI_VENDOR_ID_INTEL; dev_info->silicon_step = mvm->nvm_data->radio_cfg_step; /* TODO: Assign real value when feature is implemented */ dev_info->build_ver = 0x00; strcpy(dev_info->driver_ver, driver_ver); data_out->data = dev_info; data_out->len = sizeof(*dev_info); return 0; } static int iwl_tm_indirect_read(struct iwl_mvm *mvm, struct iwl_tm_data *data_in, struct iwl_tm_data *data_out) { struct iwl_trans *trans = mvm->trans; struct iwl_tm_sram_read_request *cmd_in = data_in->data; u32 addr = cmd_in->offset; u32 size = cmd_in->length; u32 *buf32, size32, i; unsigned long flags; if (size & (sizeof(u32)-1)) return -EINVAL; data_out->data = kmalloc(size, GFP_KERNEL); if (!data_out->data) return -ENOMEM; data_out->len = size; size32 = size / sizeof(u32); buf32 = data_out->data; mutex_lock(&mvm->mutex); /* Hard-coded periphery absolute address */ if (IWL_ABS_PRPH_START <= addr && addr < IWL_ABS_PRPH_START + PRPH_END) { if (!iwl_trans_grab_nic_access(trans, false, &flags)) { mutex_unlock(&mvm->mutex); return -EBUSY; } for (i = 0; i < size32; i++) buf32[i] = iwl_trans_read_prph(trans, addr + i * sizeof(u32)); iwl_trans_release_nic_access(trans, &flags); } else { /* target memory (SRAM) */ iwl_trans_read_mem(trans, addr, buf32, size32); } mutex_unlock(&mvm->mutex); return 0; } static int iwl_tm_indirect_write(struct iwl_mvm *mvm, struct iwl_tm_data *data_in) { struct iwl_trans *trans = mvm->trans; struct iwl_tm_sram_write_request *cmd_in = data_in->data; u32 addr = cmd_in->offset; u32 size = cmd_in->len; u8 *buf = cmd_in->buffer; u32 *buf32 = (u32 *)buf, size32 = size / sizeof(u32); unsigned long flags; u32 val, i; mutex_lock(&mvm->mutex); if (IWL_ABS_PRPH_START <= addr && addr < IWL_ABS_PRPH_START + PRPH_END) { /* Periphery writes can be 1-3 bytes long, or DWORDs */ if (size < 4) { memcpy(&val, buf, size); if (!iwl_trans_grab_nic_access(trans, false, &flags)) { mutex_unlock(&mvm->mutex); return -EBUSY; } iwl_write32(trans, HBUS_TARG_PRPH_WADDR, (addr & 0x000FFFFF) | ((size - 1) << 24)); iwl_write32(trans, HBUS_TARG_PRPH_WDAT, val); iwl_trans_release_nic_access(trans, &flags); } else { if (size % sizeof(u32)) { mutex_unlock(&mvm->mutex); return -EINVAL; } for (i = 0; i < size32; i++) iwl_write_prph(trans, addr + i*sizeof(u32), buf32[i]); } } else { iwl_trans_write_mem(trans, addr, buf32, size32); } mutex_unlock(&mvm->mutex); return 0; } /** * iwl_mvm_tm_cmd_execute - Implementation of test command executor callback * @op_mode: Specific device's operation mode * @cmd: User space command's index * @data_in: Input data. "data" field is to be casted to relevant * data structure. All verification must be done in the * caller function, therefor assuming that input data * length is valid. * @data_out: Will be allocated inside, freeing is in the caller's * responsibility */ int iwl_mvm_tm_cmd_execute(struct iwl_op_mode *op_mode, u32 cmd, struct iwl_tm_data *data_in, struct iwl_tm_data *data_out) { struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); if (WARN_ON_ONCE(!op_mode || !data_in)) return -EINVAL; switch (cmd) { case IWL_TM_USER_CMD_HCMD: return iwl_mvm_tm_send_hcmd(mvm, data_in, data_out); case IWL_TM_USER_CMD_REG_ACCESS: return iwl_mvm_tm_reg_ops(mvm->trans, data_in, data_out); case IWL_TM_USER_CMD_SRAM_WRITE: return iwl_tm_indirect_write(mvm, data_in); case IWL_TM_USER_CMD_SRAM_READ: return iwl_tm_indirect_read(mvm, data_in, data_out); case IWL_TM_USER_CMD_GET_DEVICE_INFO: return iwl_tm_get_dev_info(mvm, data_out); default: break; } return -EOPNOTSUPP; } /** * iwl_tm_mvm_send_rx() - Send a spontaneous rx message to user * @mvm: mvm opmode pointer * @rxb: Contains rx packet to be sent */ void iwl_tm_mvm_send_rx(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) { struct iwl_rx_packet *pkt; int length; pkt = rxb_addr(rxb); length = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; /* the length doesn't include len_n_flags field, so add it manually */ length += sizeof(__le32); iwl_tm_gnl_send_msg(mvm->trans, IWL_TM_USER_CMD_NOTIF_UCODE_RX_PKT, true, (void *)pkt, length, GFP_ATOMIC); }