summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/iwlwifi/pcie/trans.c
diff options
context:
space:
mode:
authorEmmanuel Grumbach <emmanuel.grumbach@intel.com>2015-07-15 23:15:08 +0300
committerEmmanuel Grumbach <emmanuel.grumbach@intel.com>2015-08-04 21:29:38 +0300
commitbd7fc617af118531e364d727ed02b0ded2bb4fc0 (patch)
tree6e25612b5c940b0e937e6eef3c170e2d2113f6ac /drivers/net/wireless/iwlwifi/pcie/trans.c
parent192de2b406ab5cab0b87be6d8015dcfbec936e37 (diff)
iwlwifi: pcie: dump RBs when FW error occurs
Add support for dumping all the RBs in the RX queue when FW error occurs. This will assist debugging. Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Diffstat (limited to 'drivers/net/wireless/iwlwifi/pcie/trans.c')
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/trans.c59
1 files changed, 57 insertions, 2 deletions
diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c
index 8cc8f2b703a2..9ebdd8009717 100644
--- a/drivers/net/wireless/iwlwifi/pcie/trans.c
+++ b/drivers/net/wireless/iwlwifi/pcie/trans.c
@@ -2275,6 +2275,47 @@ static u32 iwl_trans_pcie_dump_prph(struct iwl_trans *trans,
return prph_len;
}
+static u32 iwl_trans_pcie_dump_rbs(struct iwl_trans *trans,
+ struct iwl_fw_error_dump_data **data,
+ int allocated_rb_nums)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ int max_len = PAGE_SIZE << trans_pcie->rx_page_order;
+ struct iwl_rxq *rxq = &trans_pcie->rxq;
+ u32 i, r, j, rb_len = 0;
+
+ spin_lock(&rxq->lock);
+
+ r = le16_to_cpu(ACCESS_ONCE(rxq->rb_stts->closed_rb_num)) & 0x0FFF;
+
+ for (i = rxq->read, j = 0;
+ i != r && j < allocated_rb_nums;
+ i = (i + 1) & RX_QUEUE_MASK, j++) {
+ struct iwl_rx_mem_buffer *rxb = rxq->queue[i];
+ struct iwl_fw_error_dump_rb *rb;
+
+ dma_unmap_page(trans->dev, rxb->page_dma, max_len,
+ DMA_FROM_DEVICE);
+
+ rb_len += sizeof(**data) + sizeof(*rb) + max_len;
+
+ (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RB);
+ (*data)->len = cpu_to_le32(sizeof(*rb) + max_len);
+ rb = (void *)(*data)->data;
+ rb->index = cpu_to_le32(i);
+ memcpy(rb->data, page_address(rxb->page), max_len);
+ /* remap the page for the free benefit */
+ rxb->page_dma = dma_map_page(trans->dev, rxb->page, 0,
+ max_len,
+ DMA_FROM_DEVICE);
+
+ *data = iwl_fw_error_next_data(*data);
+ }
+
+ spin_unlock(&rxq->lock);
+
+ return rb_len;
+}
#define IWL_CSR_TO_DUMP (0x250)
static u32 iwl_trans_pcie_dump_csr(struct iwl_trans *trans,
@@ -2352,9 +2393,10 @@ struct iwl_trans_dump_data *iwl_trans_pcie_dump_data(struct iwl_trans *trans)
struct iwl_txq *cmdq = &trans_pcie->txq[trans_pcie->cmd_queue];
struct iwl_fw_error_dump_txcmd *txcmd;
struct iwl_trans_dump_data *dump_data;
- u32 len;
+ u32 len, num_rbs;
u32 monitor_len;
int i, ptr;
+ bool dump_rbs = test_bit(STATUS_FW_ERROR, &trans->status);
/* transport dump header */
len = sizeof(*dump_data);
@@ -2379,6 +2421,17 @@ struct iwl_trans_dump_data *iwl_trans_pcie_dump_data(struct iwl_trans *trans)
/* FH registers */
len += sizeof(*data) + (FH_MEM_UPPER_BOUND - FH_MEM_LOWER_BOUND);
+ if (dump_rbs) {
+ /* RBs */
+ num_rbs = le16_to_cpu(ACCESS_ONCE(
+ trans_pcie->rxq.rb_stts->closed_rb_num))
+ & 0x0FFF;
+ num_rbs = (num_rbs - trans_pcie->rxq.read) & RX_QUEUE_MASK;
+ len += num_rbs * (sizeof(*data) +
+ sizeof(struct iwl_fw_error_dump_rb) +
+ (PAGE_SIZE << trans_pcie->rx_page_order));
+ }
+
/* FW monitor */
if (trans_pcie->fw_mon_page) {
len += sizeof(*data) + sizeof(struct iwl_fw_error_dump_fw_mon) +
@@ -2442,8 +2495,10 @@ struct iwl_trans_dump_data *iwl_trans_pcie_dump_data(struct iwl_trans *trans)
len += iwl_trans_pcie_dump_prph(trans, &data);
len += iwl_trans_pcie_dump_csr(trans, &data);
len += iwl_trans_pcie_fh_regs_dump(trans, &data);
- /* data is already pointing to the next section */
+ if (dump_rbs)
+ len += iwl_trans_pcie_dump_rbs(trans, &data, num_rbs);
+ /* data is already pointing to the next section */
if ((trans_pcie->fw_mon_page &&
trans->cfg->device_family == IWL_DEVICE_FAMILY_7000) ||
trans->dbg_dest_tlv) {