u8 *pos;
 };
 
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+/**
+ * enum iwl_fw_mon_dbgfs_state - the different states of the monitor_data
+ * debugfs file
+ *
+ * @IWL_FW_MON_DBGFS_STATE_CLOSED: the file is closed.
+ * @IWL_FW_MON_DBGFS_STATE_OPEN: the file is open.
+ * @IWL_FW_MON_DBGFS_STATE_DISABLED: the file is disabled, once this state is
+ *     set the file can no longer be used.
+ */
+enum iwl_fw_mon_dbgfs_state {
+       IWL_FW_MON_DBGFS_STATE_CLOSED,
+       IWL_FW_MON_DBGFS_STATE_OPEN,
+       IWL_FW_MON_DBGFS_STATE_DISABLED,
+};
+#endif
+
 /**
  * enum iwl_shared_irq_flags - level of sharing for irq
  * @IWL_SHARED_IRQ_NON_RX: interrupt vector serves non rx causes.
        int paging_cnt;
 };
 
+/**
+ * struct cont_rec: continuous recording data structure
+ * @prev_wr_ptr: the last address that was read in monitor_data
+ *     debugfs file
+ * @prev_wrap_cnt: the wrap count that was used during the last read in
+ *     monitor_data debugfs file
+ * @state: the state of monitor_data debugfs file as described
+ *     in &iwl_fw_mon_dbgfs_state enum
+ * @mutex: locked while reading from monitor_data debugfs file
+ */
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+struct cont_rec {
+       u32 prev_wr_ptr;
+       u32 prev_wrap_cnt;
+       u8  state;
+       /* Used to sync monitor_data debugfs file with driver unload flow */
+       struct mutex mutex;
+};
+#endif
+
 /**
  * struct iwl_trans_pcie - PCIe transport specific data
  * @rxq: all the RX queue data
  * @reg_lock: protect hw register access
  * @mutex: to protect stop_device / start_fw / start_hw
  * @cmd_in_flight: true when we have a host command in flight
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ * @fw_mon_data: fw continuous recording data
+#endif
  * @msix_entries: array of MSI-X entries
  * @msix_enabled: true if managed to enable MSI-X
  * @shared_vec_mask: the type of causes the shared vector handles
        bool cmd_hold_nic_awake;
        bool ref_cmd_in_flight;
 
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       struct cont_rec fw_mon_data;
+#endif
+
        struct msix_entry msix_entries[IWL_MAX_RX_HW_QUEUES];
        bool msix_enabled;
        u8 shared_vec_mask;
 
 #include <linux/vmalloc.h>
 #include <linux/pm_runtime.h>
 #include <linux/module.h>
+#include <linux/wait.h>
 
 #include "iwl-drv.h"
 #include "iwl-trans.h"
        return count;
 }
 
+static int iwl_dbgfs_monitor_data_open(struct inode *inode,
+                                      struct file *file)
+{
+       struct iwl_trans *trans = inode->i_private;
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+       if (!trans->dbg_dest_tlv ||
+           trans->dbg_dest_tlv->monitor_mode != EXTERNAL_MODE) {
+               IWL_ERR(trans, "Debug destination is not set to DRAM\n");
+               return -ENOENT;
+       }
+
+       if (trans_pcie->fw_mon_data.state != IWL_FW_MON_DBGFS_STATE_CLOSED)
+               return -EBUSY;
+
+       trans_pcie->fw_mon_data.state = IWL_FW_MON_DBGFS_STATE_OPEN;
+       return simple_open(inode, file);
+}
+
+static int iwl_dbgfs_monitor_data_release(struct inode *inode,
+                                         struct file *file)
+{
+       struct iwl_trans_pcie *trans_pcie =
+               IWL_TRANS_GET_PCIE_TRANS(inode->i_private);
+
+       if (trans_pcie->fw_mon_data.state == IWL_FW_MON_DBGFS_STATE_OPEN)
+               trans_pcie->fw_mon_data.state = IWL_FW_MON_DBGFS_STATE_CLOSED;
+       return 0;
+}
+
+static bool iwl_write_to_user_buf(char __user *user_buf, ssize_t count,
+                                 void *buf, ssize_t *size,
+                                 ssize_t *bytes_copied)
+{
+       int buf_size_left = count - *bytes_copied;
+
+       buf_size_left = buf_size_left - (buf_size_left % sizeof(u32));
+       if (*size > buf_size_left)
+               *size = buf_size_left;
+
+       *size -= copy_to_user(user_buf, buf, *size);
+       *bytes_copied += *size;
+
+       if (buf_size_left == *size)
+               return true;
+       return false;
+}
+
+static ssize_t iwl_dbgfs_monitor_data_read(struct file *file,
+                                          char __user *user_buf,
+                                          size_t count, loff_t *ppos)
+{
+       struct iwl_trans *trans = file->private_data;
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+       void *cpu_addr = (void *)trans->fw_mon[0].block, *curr_buf;
+       struct cont_rec *data = &trans_pcie->fw_mon_data;
+       u32 write_ptr_addr, wrap_cnt_addr, write_ptr, wrap_cnt;
+       ssize_t size, bytes_copied = 0;
+       bool b_full;
+
+       if (trans->dbg_dest_tlv) {
+               write_ptr_addr =
+                       le32_to_cpu(trans->dbg_dest_tlv->write_ptr_reg);
+               wrap_cnt_addr = le32_to_cpu(trans->dbg_dest_tlv->wrap_count);
+       } else {
+               write_ptr_addr = MON_BUFF_WRPTR;
+               wrap_cnt_addr = MON_BUFF_CYCLE_CNT;
+       }
+
+       if (unlikely(!trans->dbg_rec_on))
+               return 0;
+
+       mutex_lock(&data->mutex);
+       if (data->state ==
+           IWL_FW_MON_DBGFS_STATE_DISABLED) {
+               mutex_unlock(&data->mutex);
+               return 0;
+       }
+
+       /* write_ptr position in bytes rather then DW */
+       write_ptr = iwl_read_prph(trans, write_ptr_addr) * sizeof(u32);
+       wrap_cnt = iwl_read_prph(trans, wrap_cnt_addr);
+
+       if (data->prev_wrap_cnt == wrap_cnt) {
+               size = write_ptr - data->prev_wr_ptr;
+               curr_buf = cpu_addr + data->prev_wr_ptr;
+               b_full = iwl_write_to_user_buf(user_buf, count,
+                                              curr_buf, &size,
+                                              &bytes_copied);
+               data->prev_wr_ptr += size;
+
+       } else if (data->prev_wrap_cnt == wrap_cnt - 1 &&
+                  write_ptr < data->prev_wr_ptr) {
+               size = trans->fw_mon[0].size - data->prev_wr_ptr;
+               curr_buf = cpu_addr + data->prev_wr_ptr;
+               b_full = iwl_write_to_user_buf(user_buf, count,
+                                              curr_buf, &size,
+                                              &bytes_copied);
+               data->prev_wr_ptr += size;
+
+               if (!b_full) {
+                       size = write_ptr;
+                       b_full = iwl_write_to_user_buf(user_buf, count,
+                                                      cpu_addr, &size,
+                                                      &bytes_copied);
+                       data->prev_wr_ptr = size;
+                       data->prev_wrap_cnt++;
+               }
+       } else {
+               if (data->prev_wrap_cnt == wrap_cnt - 1 &&
+                   write_ptr > data->prev_wr_ptr)
+                       IWL_WARN(trans,
+                                "write pointer passed previous write pointer, start copying from the beginning\n");
+               else if (!unlikely(data->prev_wrap_cnt == 0 &&
+                                  data->prev_wr_ptr == 0))
+                       IWL_WARN(trans,
+                                "monitor data is out of sync, start copying from the beginning\n");
+
+               size = write_ptr;
+               b_full = iwl_write_to_user_buf(user_buf, count,
+                                              cpu_addr, &size,
+                                              &bytes_copied);
+               data->prev_wr_ptr = size;
+               data->prev_wrap_cnt = wrap_cnt;
+       }
+
+       mutex_unlock(&data->mutex);
+
+       return bytes_copied;
+}
+
 DEBUGFS_READ_WRITE_FILE_OPS(interrupt);
 DEBUGFS_READ_FILE_OPS(fh_reg);
 DEBUGFS_READ_FILE_OPS(rx_queue);
 DEBUGFS_WRITE_FILE_OPS(csr);
 DEBUGFS_READ_WRITE_FILE_OPS(rfkill);
 
+static const struct file_operations iwl_dbgfs_monitor_data_ops = {
+       .read = iwl_dbgfs_monitor_data_read,
+       .open = iwl_dbgfs_monitor_data_open,
+       .release = iwl_dbgfs_monitor_data_release,
+};
+
 /* Create the debugfs files and directories */
 int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans)
 {
        DEBUGFS_ADD_FILE(csr, dir, 0200);
        DEBUGFS_ADD_FILE(fh_reg, dir, 0400);
        DEBUGFS_ADD_FILE(rfkill, dir, 0600);
+       DEBUGFS_ADD_FILE(monitor_data, dir, 0400);
        return 0;
 
 err:
        IWL_ERR(trans, "failed to create the trans debugfs entry\n");
        return -ENOMEM;
 }
+
+static void iwl_trans_pcie_debugfs_cleanup(struct iwl_trans *trans)
+{
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+       struct cont_rec *data = &trans_pcie->fw_mon_data;
+
+       mutex_lock(&data->mutex);
+       data->state = IWL_FW_MON_DBGFS_STATE_DISABLED;
+       mutex_unlock(&data->mutex);
+}
 #endif /*CONFIG_IWLWIFI_DEBUGFS */
 
 static u32 iwl_trans_pcie_get_cmdlen(struct iwl_trans *trans, void *tfd)
 
        .freeze_txq_timer = iwl_trans_pcie_freeze_txq_timer,
        .block_txq_ptrs = iwl_trans_pcie_block_txq_ptrs,
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       .debugfs_cleanup = iwl_trans_pcie_debugfs_cleanup,
+#endif
 };
 
 static const struct iwl_trans_ops trans_ops_pcie_gen2 = {
        .txq_free = iwl_trans_pcie_dyn_txq_free,
        .wait_txq_empty = iwl_trans_pcie_wait_txq_empty,
        .rxq_dma_data = iwl_trans_pcie_rxq_dma_data,
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       .debugfs_cleanup = iwl_trans_pcie_debugfs_cleanup,
+#endif
 };
 
 struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
        trans->runtime_pm_mode = IWL_PLAT_PM_MODE_DISABLED;
 #endif /* CONFIG_IWLWIFI_PCIE_RTPM */
 
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       trans_pcie->fw_mon_data.state = IWL_FW_MON_DBGFS_STATE_CLOSED;
+       mutex_init(&trans_pcie->fw_mon_data.mutex);
+#endif
+
        return trans;
 
 out_free_ict: