These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / net / wireless / iwlwifi / pcie / trans.c
index 699a480..8c72047 100644 (file)
@@ -7,6 +7,7 @@
  *
  * Copyright(c) 2007 - 2015 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2016 Intel Deutschland GmbH
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -33,6 +34,7 @@
  *
  * Copyright(c) 2005 - 2015 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2016 Intel Deutschland GmbH
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -101,14 +103,26 @@ static void iwl_pcie_free_fw_monitor(struct iwl_trans *trans)
        trans_pcie->fw_mon_size = 0;
 }
 
-static void iwl_pcie_alloc_fw_monitor(struct iwl_trans *trans)
+static void iwl_pcie_alloc_fw_monitor(struct iwl_trans *trans, u8 max_power)
 {
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        struct page *page = NULL;
        dma_addr_t phys;
-       u32 size;
+       u32 size = 0;
        u8 power;
 
+       if (!max_power) {
+               /* default max_power is maximum */
+               max_power = 26;
+       } else {
+               max_power += 11;
+       }
+
+       if (WARN(max_power > 26,
+                "External buffer size for monitor is too big %d, check the FW TLV\n",
+                max_power))
+               return;
+
        if (trans_pcie->fw_mon_page) {
                dma_sync_single_for_device(trans->dev, trans_pcie->fw_mon_phys,
                                           trans_pcie->fw_mon_size,
@@ -117,7 +131,7 @@ static void iwl_pcie_alloc_fw_monitor(struct iwl_trans *trans)
        }
 
        phys = 0;
-       for (power = 26; power >= 11; power--) {
+       for (power = max_power; power >= 11; power--) {
                int order;
 
                size = BIT(power);
@@ -143,6 +157,12 @@ static void iwl_pcie_alloc_fw_monitor(struct iwl_trans *trans)
        if (WARN_ON_ONCE(!page))
                return;
 
+       if (power != max_power)
+               IWL_ERR(trans,
+                       "Sorry - debug buffer is only %luK while you requested %luK\n",
+                       (unsigned long)BIT(power - 10),
+                       (unsigned long)BIT(max_power - 10));
+
        trans_pcie->fw_mon_page = page;
        trans_pcie->fw_mon_phys = phys;
        trans_pcie->fw_mon_size = size;
@@ -164,6 +184,9 @@ static void iwl_trans_pcie_write_shr(struct iwl_trans *trans, u32 reg, u32 val)
 
 static void iwl_pcie_set_pwr(struct iwl_trans *trans, bool vaux)
 {
+       if (trans->cfg->apmg_not_supported)
+               return;
+
        if (vaux && pci_pme_capable(to_pci_dev(trans->dev), PCI_D3cold))
                iwl_set_bits_mask_prph(trans, APMG_PS_CTRL_REG,
                                       APMG_PS_CTRL_VAL_PWR_SRC_VAUX,
@@ -297,7 +320,7 @@ static int iwl_pcie_apm_init(struct iwl_trans *trans)
         * bits do not disable clocks.  This preserves any hardware
         * bits already set by default in "CLK_CTRL_REG" after reset.
         */
-       if (trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) {
+       if (!trans->cfg->apmg_not_supported) {
                iwl_write_prph(trans, APMG_CLK_EN_REG,
                               APMG_CLK_VAL_DMA_CLK_RQT);
                udelay(20);
@@ -503,8 +526,7 @@ static int iwl_pcie_nic_init(struct iwl_trans *trans)
 
        spin_unlock(&trans_pcie->irq_lock);
 
-       if (trans->cfg->device_family != IWL_DEVICE_FAMILY_8000)
-               iwl_pcie_set_pwr(trans, false);
+       iwl_pcie_set_pwr(trans, false);
 
        iwl_op_mode_nic_config(trans->op_mode);
 
@@ -572,10 +594,8 @@ static int iwl_pcie_prepare_card_hw(struct iwl_trans *trans)
 
                do {
                        ret = iwl_pcie_set_hw_ready(trans);
-                       if (ret >= 0) {
-                               ret = 0;
-                               goto out;
-                       }
+                       if (ret >= 0)
+                               return 0;
 
                        usleep_range(200, 1000);
                        t += 200;
@@ -585,10 +605,6 @@ static int iwl_pcie_prepare_card_hw(struct iwl_trans *trans)
 
        IWL_ERR(trans, "Couldn't prepare the card\n");
 
-out:
-       iwl_clear_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG,
-                     CSR_RESET_LINK_PWR_MGMT_DISABLED);
-
        return ret;
 }
 
@@ -760,8 +776,15 @@ static int iwl_pcie_load_cpu_sections_8000(struct iwl_trans *trans,
        for (i = *first_ucode_section; i < IWL_UCODE_SECTION_MAX; i++) {
                last_read_idx = i;
 
+               /*
+                * CPU1_CPU2_SEPARATOR_SECTION delimiter - separate between
+                * CPU1 to CPU2.
+                * PAGING_SEPARATOR_SECTION delimiter - separate between
+                * CPU2 non paged to CPU2 paging sec.
+                */
                if (!image->sec[i].data ||
-                   image->sec[i].offset == CPU1_CPU2_SEPARATOR_SECTION) {
+                   image->sec[i].offset == CPU1_CPU2_SEPARATOR_SECTION ||
+                   image->sec[i].offset == PAGING_SEPARATOR_SECTION) {
                        IWL_DEBUG_FW(trans,
                                     "Break since Data not valid or Empty section, sec = %d\n",
                                     i);
@@ -809,8 +832,15 @@ static int iwl_pcie_load_cpu_sections(struct iwl_trans *trans,
        for (i = *first_ucode_section; i < IWL_UCODE_SECTION_MAX; i++) {
                last_read_idx = i;
 
+               /*
+                * CPU1_CPU2_SEPARATOR_SECTION delimiter - separate between
+                * CPU1 to CPU2.
+                * PAGING_SEPARATOR_SECTION delimiter - separate between
+                * CPU2 non paged to CPU2 paging sec.
+                */
                if (!image->sec[i].data ||
-                   image->sec[i].offset == CPU1_CPU2_SEPARATOR_SECTION) {
+                   image->sec[i].offset == CPU1_CPU2_SEPARATOR_SECTION ||
+                   image->sec[i].offset == PAGING_SEPARATOR_SECTION) {
                        IWL_DEBUG_FW(trans,
                                     "Break since Data not valid or Empty section, sec = %d\n",
                                     i);
@@ -850,7 +880,7 @@ static void iwl_pcie_apply_destination(struct iwl_trans *trans)
                 get_fw_dbg_mode_string(dest->monitor_mode));
 
        if (dest->monitor_mode == EXTERNAL_MODE)
-               iwl_pcie_alloc_fw_monitor(trans);
+               iwl_pcie_alloc_fw_monitor(trans, dest->size_power);
        else
                IWL_WARN(trans, "PCI should have external buffer debug\n");
 
@@ -877,6 +907,14 @@ static void iwl_pcie_apply_destination(struct iwl_trans *trans)
                case PRPH_CLEARBIT:
                        iwl_clear_bits_prph(trans, addr, BIT(val));
                        break;
+               case PRPH_BLOCKBIT:
+                       if (iwl_read_prph(trans, addr) & BIT(val)) {
+                               IWL_ERR(trans,
+                                       "BIT(%u) in address 0x%x is 1, stopping FW configuration\n",
+                                       val, addr);
+                               goto monitor;
+                       }
+                       break;
                default:
                        IWL_ERR(trans, "FW debug - unknown OP %d\n",
                                dest->reg_ops[i].op);
@@ -884,12 +922,20 @@ static void iwl_pcie_apply_destination(struct iwl_trans *trans)
                }
        }
 
+monitor:
        if (dest->monitor_mode == EXTERNAL_MODE && trans_pcie->fw_mon_size) {
                iwl_write_prph(trans, le32_to_cpu(dest->base_reg),
                               trans_pcie->fw_mon_phys >> dest->base_shift);
-               iwl_write_prph(trans, le32_to_cpu(dest->end_reg),
-                              (trans_pcie->fw_mon_phys +
-                               trans_pcie->fw_mon_size) >> dest->end_shift);
+               if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
+                       iwl_write_prph(trans, le32_to_cpu(dest->end_reg),
+                                      (trans_pcie->fw_mon_phys +
+                                       trans_pcie->fw_mon_size - 256) >>
+                                               dest->end_shift);
+               else
+                       iwl_write_prph(trans, le32_to_cpu(dest->end_reg),
+                                      (trans_pcie->fw_mon_phys +
+                                       trans_pcie->fw_mon_size) >>
+                                               dest->end_shift);
        }
 }
 
@@ -924,7 +970,7 @@ static int iwl_pcie_load_given_ucode(struct iwl_trans *trans,
        /* supported for 7000 only for the moment */
        if (iwlwifi_mod_params.fw_monitor &&
            trans->cfg->device_family == IWL_DEVICE_FAMILY_7000) {
-               iwl_pcie_alloc_fw_monitor(trans);
+               iwl_pcie_alloc_fw_monitor(trans, 0);
 
                if (trans_pcie->fw_mon_size) {
                        iwl_write_prph(trans, MON_BUFF_BASE_ADDR,
@@ -971,24 +1017,32 @@ static int iwl_pcie_load_given_ucode_8000(struct iwl_trans *trans,
                return ret;
 
        /* load to FW the binary sections of CPU2 */
-       ret = iwl_pcie_load_cpu_sections_8000(trans, image, 2,
-                                             &first_ucode_section);
-       if (ret)
-               return ret;
-
-       return 0;
+       return iwl_pcie_load_cpu_sections_8000(trans, image, 2,
+                                              &first_ucode_section);
 }
 
 static int iwl_trans_pcie_start_fw(struct iwl_trans *trans,
                                   const struct fw_img *fw, bool run_in_rfkill)
 {
-       int ret;
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        bool hw_rfkill;
+       int ret;
+
+       mutex_lock(&trans_pcie->mutex);
+
+       /* Someone called stop_device, don't try to start_fw */
+       if (trans_pcie->is_down) {
+               IWL_WARN(trans,
+                        "Can't start_fw since the HW hasn't been started\n");
+               ret = EIO;
+               goto out;
+       }
 
        /* This may fail if AMT took ownership of the device */
        if (iwl_pcie_prepare_card_hw(trans)) {
                IWL_WARN(trans, "Exit HW not ready\n");
-               return -EIO;
+               ret = -EIO;
+               goto out;
        }
 
        iwl_enable_rfkill_int(trans);
@@ -1000,15 +1054,17 @@ static int iwl_trans_pcie_start_fw(struct iwl_trans *trans,
        else
                clear_bit(STATUS_RFKILL, &trans->status);
        iwl_trans_pcie_rf_kill(trans, hw_rfkill);
-       if (hw_rfkill && !run_in_rfkill)
-               return -ERFKILL;
+       if (hw_rfkill && !run_in_rfkill) {
+               ret = -ERFKILL;
+               goto out;
+       }
 
        iwl_write32(trans, CSR_INT, 0xFFFFFFFF);
 
        ret = iwl_pcie_nic_init(trans);
        if (ret) {
                IWL_ERR(trans, "Unable to init nic\n");
-               return ret;
+               goto out;
        }
 
        /* make sure rfkill handshake bits are cleared */
@@ -1026,9 +1082,13 @@ static int iwl_trans_pcie_start_fw(struct iwl_trans *trans,
 
        /* Load the given image to the HW */
        if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
-               return iwl_pcie_load_given_ucode_8000(trans, fw);
+               ret = iwl_pcie_load_given_ucode_8000(trans, fw);
        else
-               return iwl_pcie_load_given_ucode(trans, fw);
+               ret = iwl_pcie_load_given_ucode(trans, fw);
+
+out:
+       mutex_unlock(&trans_pcie->mutex);
+       return ret;
 }
 
 static void iwl_trans_pcie_fw_alive(struct iwl_trans *trans, u32 scd_addr)
@@ -1037,11 +1097,18 @@ static void iwl_trans_pcie_fw_alive(struct iwl_trans *trans, u32 scd_addr)
        iwl_pcie_tx_start(trans, scd_addr);
 }
 
-static void iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool low_power)
+static void _iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool low_power)
 {
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        bool hw_rfkill, was_hw_rfkill;
 
+       lockdep_assert_held(&trans_pcie->mutex);
+
+       if (trans_pcie->is_down)
+               return;
+
+       trans_pcie->is_down = true;
+
        was_hw_rfkill = iwl_is_rfkill_set(trans);
 
        /* tell the device to stop sending interrupts */
@@ -1065,7 +1132,7 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool low_power)
                iwl_pcie_rx_stop(trans);
 
                /* Power-down device's busmaster DMA clocks */
-               if (trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) {
+               if (!trans->cfg->apmg_not_supported) {
                        iwl_write_prph(trans, APMG_CLK_DIS_REG,
                                       APMG_CLK_VAL_DMA_CLK_RQT);
                        udelay(5);
@@ -1131,14 +1198,36 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool low_power)
        iwl_pcie_prepare_card_hw(trans);
 }
 
+static void iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool low_power)
+{
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+       mutex_lock(&trans_pcie->mutex);
+       _iwl_trans_pcie_stop_device(trans, low_power);
+       mutex_unlock(&trans_pcie->mutex);
+}
+
 void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state)
 {
+       struct iwl_trans_pcie __maybe_unused *trans_pcie =
+               IWL_TRANS_GET_PCIE_TRANS(trans);
+
+       lockdep_assert_held(&trans_pcie->mutex);
+
        if (iwl_op_mode_hw_rf_kill(trans->op_mode, state))
-               iwl_trans_pcie_stop_device(trans, true);
+               _iwl_trans_pcie_stop_device(trans, true);
 }
 
 static void iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool test)
 {
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+       if (trans->wowlan_d0i3) {
+               /* Enable persistence mode to avoid reset */
+               iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG,
+                           CSR_HW_IF_CONFIG_REG_PERSIST_MODE);
+       }
+
        iwl_disable_interrupts(trans);
 
        /*
@@ -1150,17 +1239,21 @@ static void iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool test)
 
        iwl_pcie_disable_ict(trans);
 
+       synchronize_irq(trans_pcie->pci_dev->irq);
+
        iwl_clear_bit(trans, CSR_GP_CNTRL,
                      CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
        iwl_clear_bit(trans, CSR_GP_CNTRL,
                      CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
 
-       /*
-        * reset TX queues -- some of their registers reset during S3
-        * so if we don't reset everything here the D3 image would try
-        * to execute some invalid memory upon resume
-        */
-       iwl_trans_pcie_tx_reset(trans);
+       if (!trans->wowlan_d0i3) {
+               /*
+                * reset TX queues -- some of their registers reset during S3
+                * so if we don't reset everything here the D3 image would try
+                * to execute some invalid memory upon resume
+                */
+               iwl_trans_pcie_tx_reset(trans);
+       }
 
        iwl_pcie_set_pwr(trans, true);
 }
@@ -1202,12 +1295,18 @@ static int iwl_trans_pcie_d3_resume(struct iwl_trans *trans,
 
        iwl_pcie_set_pwr(trans, false);
 
-       iwl_trans_pcie_tx_reset(trans);
+       if (trans->wowlan_d0i3) {
+               iwl_clear_bit(trans, CSR_GP_CNTRL,
+                             CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+       } else {
+               iwl_trans_pcie_tx_reset(trans);
 
-       ret = iwl_pcie_rx_init(trans);
-       if (ret) {
-               IWL_ERR(trans, "Failed to resume the device (RX reset)\n");
-               return ret;
+               ret = iwl_pcie_rx_init(trans);
+               if (ret) {
+                       IWL_ERR(trans,
+                               "Failed to resume the device (RX reset)\n");
+                       return ret;
+               }
        }
 
        val = iwl_read32(trans, CSR_RESET);
@@ -1219,11 +1318,14 @@ static int iwl_trans_pcie_d3_resume(struct iwl_trans *trans,
        return 0;
 }
 
-static int iwl_trans_pcie_start_hw(struct iwl_trans *trans, bool low_power)
+static int _iwl_trans_pcie_start_hw(struct iwl_trans *trans, bool low_power)
 {
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        bool hw_rfkill;
        int err;
 
+       lockdep_assert_held(&trans_pcie->mutex);
+
        err = iwl_pcie_prepare_card_hw(trans);
        if (err) {
                IWL_ERR(trans, "Error while preparing HW: %d\n", err);
@@ -1240,20 +1342,38 @@ static int iwl_trans_pcie_start_hw(struct iwl_trans *trans, bool low_power)
        /* From now on, the op_mode will be kept updated about RF kill state */
        iwl_enable_rfkill_int(trans);
 
+       /* Set is_down to false here so that...*/
+       trans_pcie->is_down = false;
+
        hw_rfkill = iwl_is_rfkill_set(trans);
        if (hw_rfkill)
                set_bit(STATUS_RFKILL, &trans->status);
        else
                clear_bit(STATUS_RFKILL, &trans->status);
+       /* ... rfkill can call stop_device and set it false if needed */
        iwl_trans_pcie_rf_kill(trans, hw_rfkill);
 
        return 0;
 }
 
+static int iwl_trans_pcie_start_hw(struct iwl_trans *trans, bool low_power)
+{
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+       int ret;
+
+       mutex_lock(&trans_pcie->mutex);
+       ret = _iwl_trans_pcie_start_hw(trans, low_power);
+       mutex_unlock(&trans_pcie->mutex);
+
+       return ret;
+}
+
 static void iwl_trans_pcie_op_mode_leave(struct iwl_trans *trans)
 {
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
 
+       mutex_lock(&trans_pcie->mutex);
+
        /* disable interrupts - don't enable HW RF kill interrupt */
        spin_lock(&trans_pcie->irq_lock);
        iwl_disable_interrupts(trans);
@@ -1266,6 +1386,10 @@ static void iwl_trans_pcie_op_mode_leave(struct iwl_trans *trans)
        spin_unlock(&trans_pcie->irq_lock);
 
        iwl_pcie_disable_ict(trans);
+
+       mutex_unlock(&trans_pcie->mutex);
+
+       synchronize_irq(trans_pcie->pci_dev->irq);
 }
 
 static void iwl_trans_pcie_write8(struct iwl_trans *trans, u32 ofs, u8 val)
@@ -1326,6 +1450,7 @@ static void iwl_trans_pcie_configure(struct iwl_trans *trans,
        else
                trans_pcie->rx_page_order = get_order(4 * 1024);
 
+       trans_pcie->wide_cmd_header = trans_cfg->wide_cmd_header;
        trans_pcie->command_names = trans_cfg->command_names;
        trans_pcie->bc_table_dword = trans_cfg->bc_table_dword;
        trans_pcie->scd_set_active = trans_cfg->scd_set_active;
@@ -1338,11 +1463,10 @@ static void iwl_trans_pcie_configure(struct iwl_trans *trans,
         * As this function may be called again in some corner cases don't
         * do anything if NAPI was already initialized.
         */
-       if (!trans_pcie->napi.poll && trans->op_mode->ops->napi_add) {
+       if (!trans_pcie->napi.poll) {
                init_dummy_netdev(&trans_pcie->napi_dev);
-               iwl_op_mode_napi_add(trans->op_mode, &trans_pcie->napi,
-                                    &trans_pcie->napi_dev,
-                                    iwl_pcie_dummy_napi_poll, 64);
+               netif_napi_add(&trans_pcie->napi_dev, &trans_pcie->napi,
+                              iwl_pcie_dummy_napi_poll, 64);
        }
 }
 
@@ -1362,14 +1486,13 @@ void iwl_trans_pcie_free(struct iwl_trans *trans)
        iounmap(trans_pcie->hw_base);
        pci_release_regions(trans_pcie->pci_dev);
        pci_disable_device(trans_pcie->pci_dev);
-       kmem_cache_destroy(trans->dev_cmd_pool);
 
        if (trans_pcie->napi.poll)
                netif_napi_del(&trans_pcie->napi);
 
        iwl_pcie_free_fw_monitor(trans);
 
-       kfree(trans);
+       iwl_trans_free(trans);
 }
 
 static void iwl_trans_pcie_set_pmi(struct iwl_trans *trans, bool state)
@@ -2170,6 +2293,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,
@@ -2216,17 +2380,120 @@ static u32 iwl_trans_pcie_fh_regs_dump(struct iwl_trans *trans,
        return sizeof(**data) + fh_regs_len;
 }
 
-static
-struct iwl_trans_dump_data *iwl_trans_pcie_dump_data(struct iwl_trans *trans)
+static u32
+iwl_trans_pci_dump_marbh_monitor(struct iwl_trans *trans,
+                                struct iwl_fw_error_dump_fw_mon *fw_mon_data,
+                                u32 monitor_len)
+{
+       u32 buf_size_in_dwords = (monitor_len >> 2);
+       u32 *buffer = (u32 *)fw_mon_data->data;
+       unsigned long flags;
+       u32 i;
+
+       if (!iwl_trans_grab_nic_access(trans, false, &flags))
+               return 0;
+
+       __iwl_write_prph(trans, MON_DMARB_RD_CTL_ADDR, 0x1);
+       for (i = 0; i < buf_size_in_dwords; i++)
+               buffer[i] = __iwl_read_prph(trans, MON_DMARB_RD_DATA_ADDR);
+       __iwl_write_prph(trans, MON_DMARB_RD_CTL_ADDR, 0x0);
+
+       iwl_trans_release_nic_access(trans, &flags);
+
+       return monitor_len;
+}
+
+static u32
+iwl_trans_pcie_dump_monitor(struct iwl_trans *trans,
+                           struct iwl_fw_error_dump_data **data,
+                           u32 monitor_len)
+{
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+       u32 len = 0;
+
+       if ((trans_pcie->fw_mon_page &&
+            trans->cfg->device_family == IWL_DEVICE_FAMILY_7000) ||
+           trans->dbg_dest_tlv) {
+               struct iwl_fw_error_dump_fw_mon *fw_mon_data;
+               u32 base, write_ptr, wrap_cnt;
+
+               /* If there was a dest TLV - use the values from there */
+               if (trans->dbg_dest_tlv) {
+                       write_ptr =
+                               le32_to_cpu(trans->dbg_dest_tlv->write_ptr_reg);
+                       wrap_cnt = le32_to_cpu(trans->dbg_dest_tlv->wrap_count);
+                       base = le32_to_cpu(trans->dbg_dest_tlv->base_reg);
+               } else {
+                       base = MON_BUFF_BASE_ADDR;
+                       write_ptr = MON_BUFF_WRPTR;
+                       wrap_cnt = MON_BUFF_CYCLE_CNT;
+               }
+
+               (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_FW_MONITOR);
+               fw_mon_data = (void *)(*data)->data;
+               fw_mon_data->fw_mon_wr_ptr =
+                       cpu_to_le32(iwl_read_prph(trans, write_ptr));
+               fw_mon_data->fw_mon_cycle_cnt =
+                       cpu_to_le32(iwl_read_prph(trans, wrap_cnt));
+               fw_mon_data->fw_mon_base_ptr =
+                       cpu_to_le32(iwl_read_prph(trans, base));
+
+               len += sizeof(**data) + sizeof(*fw_mon_data);
+               if (trans_pcie->fw_mon_page) {
+                       /*
+                        * The firmware is now asserted, it won't write anything
+                        * to the buffer. CPU can take ownership to fetch the
+                        * data. The buffer will be handed back to the device
+                        * before the firmware will be restarted.
+                        */
+                       dma_sync_single_for_cpu(trans->dev,
+                                               trans_pcie->fw_mon_phys,
+                                               trans_pcie->fw_mon_size,
+                                               DMA_FROM_DEVICE);
+                       memcpy(fw_mon_data->data,
+                              page_address(trans_pcie->fw_mon_page),
+                              trans_pcie->fw_mon_size);
+
+                       monitor_len = trans_pcie->fw_mon_size;
+               } else if (trans->dbg_dest_tlv->monitor_mode == SMEM_MODE) {
+                       /*
+                        * Update pointers to reflect actual values after
+                        * shifting
+                        */
+                       base = iwl_read_prph(trans, base) <<
+                              trans->dbg_dest_tlv->base_shift;
+                       iwl_trans_read_mem(trans, base, fw_mon_data->data,
+                                          monitor_len / sizeof(u32));
+               } else if (trans->dbg_dest_tlv->monitor_mode == MARBH_MODE) {
+                       monitor_len =
+                               iwl_trans_pci_dump_marbh_monitor(trans,
+                                                                fw_mon_data,
+                                                                monitor_len);
+               } else {
+                       /* Didn't match anything - output no monitor data */
+                       monitor_len = 0;
+               }
+
+               len += monitor_len;
+               (*data)->len = cpu_to_le32(monitor_len + sizeof(*fw_mon_data));
+       }
+
+       return len;
+}
+
+static struct iwl_trans_dump_data
+*iwl_trans_pcie_dump_data(struct iwl_trans *trans,
+                         struct iwl_fw_dbg_trigger_tlv *trigger)
 {
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        struct iwl_fw_error_dump_data *data;
        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);
@@ -2235,22 +2502,6 @@ struct iwl_trans_dump_data *iwl_trans_pcie_dump_data(struct iwl_trans *trans)
        len += sizeof(*data) +
                cmdq->q.n_window * (sizeof(*txcmd) + TFD_MAX_PAYLOAD_SIZE);
 
-       /* CSR registers */
-       len += sizeof(*data) + IWL_CSR_TO_DUMP;
-
-       /* PRPH registers */
-       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;
-
-               len += sizeof(*data) + sizeof(struct iwl_fw_error_dump_prph) +
-                       num_bytes_in_chunk;
-       }
-
-       /* FH registers */
-       len += sizeof(*data) + (FH_MEM_UPPER_BOUND - FH_MEM_LOWER_BOUND);
-
        /* FW monitor */
        if (trans_pcie->fw_mon_page) {
                len += sizeof(*data) + sizeof(struct iwl_fw_error_dump_fw_mon) +
@@ -2268,7 +2519,8 @@ struct iwl_trans_dump_data *iwl_trans_pcie_dump_data(struct iwl_trans *trans)
                      trans->dbg_dest_tlv->end_shift;
 
                /* Make "end" point to the actual end */
-               if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
+               if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000 ||
+                   trans->dbg_dest_tlv->monitor_mode == MARBH_MODE)
                        end += (1 << trans->dbg_dest_tlv->end_shift);
                monitor_len = end - base;
                len += sizeof(*data) + sizeof(struct iwl_fw_error_dump_fw_mon) +
@@ -2277,6 +2529,45 @@ struct iwl_trans_dump_data *iwl_trans_pcie_dump_data(struct iwl_trans *trans)
                monitor_len = 0;
        }
 
+       if (trigger && (trigger->mode & IWL_FW_DBG_TRIGGER_MONITOR_ONLY)) {
+               dump_data = vzalloc(len);
+               if (!dump_data)
+                       return NULL;
+
+               data = (void *)dump_data->data;
+               len = iwl_trans_pcie_dump_monitor(trans, &data, monitor_len);
+               dump_data->len = len;
+
+               return dump_data;
+       }
+
+       /* CSR registers */
+       len += sizeof(*data) + IWL_CSR_TO_DUMP;
+
+       /* PRPH registers */
+       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;
+
+               len += sizeof(*data) + sizeof(struct iwl_fw_error_dump_prph) +
+                      num_bytes_in_chunk;
+       }
+
+       /* 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));
+       }
+
        dump_data = vzalloc(len);
        if (!dump_data)
                return NULL;
@@ -2313,71 +2604,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 ((trans_pcie->fw_mon_page &&
-            trans->cfg->device_family == IWL_DEVICE_FAMILY_7000) ||
-           trans->dbg_dest_tlv) {
-               struct iwl_fw_error_dump_fw_mon *fw_mon_data;
-               u32 base, write_ptr, wrap_cnt;
-
-               /* If there was a dest TLV - use the values from there */
-               if (trans->dbg_dest_tlv) {
-                       write_ptr =
-                               le32_to_cpu(trans->dbg_dest_tlv->write_ptr_reg);
-                       wrap_cnt = le32_to_cpu(trans->dbg_dest_tlv->wrap_count);
-                       base = le32_to_cpu(trans->dbg_dest_tlv->base_reg);
-               } else {
-                       base = MON_BUFF_BASE_ADDR;
-                       write_ptr = MON_BUFF_WRPTR;
-                       wrap_cnt = MON_BUFF_CYCLE_CNT;
-               }
-
-               data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_FW_MONITOR);
-               fw_mon_data = (void *)data->data;
-               fw_mon_data->fw_mon_wr_ptr =
-                       cpu_to_le32(iwl_read_prph(trans, write_ptr));
-               fw_mon_data->fw_mon_cycle_cnt =
-                       cpu_to_le32(iwl_read_prph(trans, wrap_cnt));
-               fw_mon_data->fw_mon_base_ptr =
-                       cpu_to_le32(iwl_read_prph(trans, base));
-
-               len += sizeof(*data) + sizeof(*fw_mon_data);
-               if (trans_pcie->fw_mon_page) {
-                       data->len = cpu_to_le32(trans_pcie->fw_mon_size +
-                                               sizeof(*fw_mon_data));
-
-                       /*
-                        * The firmware is now asserted, it won't write anything
-                        * to the buffer. CPU can take ownership to fetch the
-                        * data. The buffer will be handed back to the device
-                        * before the firmware will be restarted.
-                        */
-                       dma_sync_single_for_cpu(trans->dev,
-                                               trans_pcie->fw_mon_phys,
-                                               trans_pcie->fw_mon_size,
-                                               DMA_FROM_DEVICE);
-                       memcpy(fw_mon_data->data,
-                              page_address(trans_pcie->fw_mon_page),
-                              trans_pcie->fw_mon_size);
+       if (dump_rbs)
+               len += iwl_trans_pcie_dump_rbs(trans, &data, num_rbs);
 
-                       len += trans_pcie->fw_mon_size;
-               } else {
-                       /* If we are here then the buffer is internal */
-
-                       /*
-                        * Update pointers to reflect actual values after
-                        * shifting
-                        */
-                       base = iwl_read_prph(trans, base) <<
-                              trans->dbg_dest_tlv->base_shift;
-                       iwl_trans_read_mem(trans, base, fw_mon_data->data,
-                                          monitor_len / sizeof(u32));
-                       data->len = cpu_to_le32(sizeof(*fw_mon_data) +
-                                               monitor_len);
-                       len += monitor_len;
-               }
-       }
+       len += iwl_trans_pcie_dump_monitor(trans, &data, monitor_len);
 
        dump_data->len = len;
 
@@ -2433,28 +2663,26 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
        struct iwl_trans_pcie *trans_pcie;
        struct iwl_trans *trans;
        u16 pci_cmd;
-       int err;
+       int ret;
 
-       trans = kzalloc(sizeof(struct iwl_trans) +
-                       sizeof(struct iwl_trans_pcie), GFP_KERNEL);
-       if (!trans) {
-               err = -ENOMEM;
-               goto out;
-       }
+       trans = iwl_trans_alloc(sizeof(struct iwl_trans_pcie),
+                               &pdev->dev, cfg, &trans_ops_pcie, 0);
+       if (!trans)
+               return ERR_PTR(-ENOMEM);
+
+       trans->max_skb_frags = IWL_PCIE_MAX_FRAGS;
 
        trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
 
-       trans->ops = &trans_ops_pcie;
-       trans->cfg = cfg;
-       trans_lockdep_init(trans);
        trans_pcie->trans = trans;
        spin_lock_init(&trans_pcie->irq_lock);
        spin_lock_init(&trans_pcie->reg_lock);
        spin_lock_init(&trans_pcie->ref_lock);
+       mutex_init(&trans_pcie->mutex);
        init_waitqueue_head(&trans_pcie->ucode_write_waitq);
 
-       err = pci_enable_device(pdev);
-       if (err)
+       ret = pci_enable_device(pdev);
+       if (ret)
                goto out_no_pci;
 
        if (!cfg->base_params->pcie_l1_allowed) {
@@ -2470,23 +2698,23 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
 
        pci_set_master(pdev);
 
-       err = pci_set_dma_mask(pdev, DMA_BIT_MASK(36));
-       if (!err)
-               err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(36));
-       if (err) {
-               err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
-               if (!err)
-                       err = pci_set_consistent_dma_mask(pdev,
+       ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(36));
+       if (!ret)
+               ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(36));
+       if (ret) {
+               ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+               if (!ret)
+                       ret = pci_set_consistent_dma_mask(pdev,
                                                          DMA_BIT_MASK(32));
                /* both attempts failed: */
-               if (err) {
+               if (ret) {
                        dev_err(&pdev->dev, "No suitable DMA available\n");
                        goto out_pci_disable_device;
                }
        }
 
-       err = pci_request_regions(pdev, DRV_NAME);
-       if (err) {
+       ret = pci_request_regions(pdev, DRV_NAME);
+       if (ret) {
                dev_err(&pdev->dev, "pci_request_regions failed\n");
                goto out_pci_disable_device;
        }
@@ -2494,7 +2722,7 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
        trans_pcie->hw_base = pci_ioremap_bar(pdev, 0);
        if (!trans_pcie->hw_base) {
                dev_err(&pdev->dev, "pci_ioremap_bar failed\n");
-               err = -ENODEV;
+               ret = -ENODEV;
                goto out_pci_release_regions;
        }
 
@@ -2506,9 +2734,9 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
        trans_pcie->pci_dev = pdev;
        iwl_disable_interrupts(trans);
 
-       err = pci_enable_msi(pdev);
-       if (err) {
-               dev_err(&pdev->dev, "pci_enable_msi failed(0X%x)\n", err);
+       ret = pci_enable_msi(pdev);
+       if (ret) {
+               dev_err(&pdev->dev, "pci_enable_msi failed(0X%x)\n", ret);
                /* enable rfkill interrupt: hw bug w/a */
                pci_read_config_word(pdev, PCI_COMMAND, &pci_cmd);
                if (pci_cmd & PCI_COMMAND_INTX_DISABLE) {
@@ -2526,7 +2754,6 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
         */
        if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) {
                unsigned long flags;
-               int ret;
 
                trans->hw_rev = (trans->hw_rev & 0xfff0) |
                                (CSR_HW_REV_STEP(trans->hw_rev << 2) << 2);
@@ -2576,30 +2803,14 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
        /* Initialize the wait queue for commands */
        init_waitqueue_head(&trans_pcie->wait_command_queue);
 
-       snprintf(trans->dev_cmd_pool_name, sizeof(trans->dev_cmd_pool_name),
-                "iwl_cmd_pool:%s", dev_name(trans->dev));
-
-       trans->dev_cmd_headroom = 0;
-       trans->dev_cmd_pool =
-               kmem_cache_create(trans->dev_cmd_pool_name,
-                                 sizeof(struct iwl_device_cmd)
-                                 + trans->dev_cmd_headroom,
-                                 sizeof(void *),
-                                 SLAB_HWCACHE_ALIGN,
-                                 NULL);
-
-       if (!trans->dev_cmd_pool) {
-               err = -ENOMEM;
+       ret = iwl_pcie_alloc_ict(trans);
+       if (ret)
                goto out_pci_disable_msi;
-       }
 
-       if (iwl_pcie_alloc_ict(trans))
-               goto out_free_cmd_pool;
-
-       err = request_threaded_irq(pdev->irq, iwl_pcie_isr,
+       ret = request_threaded_irq(pdev->irq, iwl_pcie_isr,
                                   iwl_pcie_irq_handler,
                                   IRQF_SHARED, DRV_NAME, trans);
-       if (err) {
+       if (ret) {
                IWL_ERR(trans, "Error allocating IRQ %d\n", pdev->irq);
                goto out_free_ict;
        }
@@ -2611,8 +2822,6 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
 
 out_free_ict:
        iwl_pcie_free_ict(trans);
-out_free_cmd_pool:
-       kmem_cache_destroy(trans->dev_cmd_pool);
 out_pci_disable_msi:
        pci_disable_msi(pdev);
 out_pci_release_regions:
@@ -2620,7 +2829,6 @@ out_pci_release_regions:
 out_pci_disable_device:
        pci_disable_device(pdev);
 out_no_pci:
-       kfree(trans);
-out:
-       return ERR_PTR(err);
+       iwl_trans_free(trans);
+       return ERR_PTR(ret);
 }