aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/iwlwifi
diff options
context:
space:
mode:
authorEmmanuel Grumbach <emmanuel.grumbach@intel.com>2014-06-01 01:05:52 -0400
committerEmmanuel Grumbach <emmanuel.grumbach@intel.com>2014-06-24 14:55:27 -0400
commitc2d202017da18ebd6567862bd9a50392970f048f (patch)
tree538cd364c5bf39d0a1b230dbbfdb935e2280f147 /drivers/net/wireless/iwlwifi
parent1fa1605648d15d42f350807279b6c6e8d33b6382 (diff)
iwlwifi: pcie: add firmware monitor capabilities
This allows to use the firmware monitor. This capability uses a lot of contiguous memory (up to 64MB), so make its usage module parameter dependent. The driver will try to allocate as much contiguous memory as possible downgrading its requirements until the allocation succeeds. Dump this data into the fw-error dump file when an error happens. Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Diffstat (limited to 'drivers/net/wireless/iwlwifi')
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-drv.c4
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h18
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-modparams.h2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-prph.h6
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/internal.h7
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/trans.c125
6 files changed, 158 insertions, 4 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c
index 01a426871ffb..bb842f4732dd 100644
--- a/drivers/net/wireless/iwlwifi/iwl-drv.c
+++ b/drivers/net/wireless/iwlwifi/iwl-drv.c
@@ -1405,3 +1405,7 @@ module_param_named(power_level, iwlwifi_mod_params.power_level,
1405 int, S_IRUGO); 1405 int, S_IRUGO);
1406MODULE_PARM_DESC(power_level, 1406MODULE_PARM_DESC(power_level,
1407 "default power save level (range from 1 - 5, default: 1)"); 1407 "default power save level (range from 1 - 5, default: 1)");
1408
1409module_param_named(fw_monitor, iwlwifi_mod_params.fw_monitor, bool, S_IRUGO);
1410MODULE_PARM_DESC(dbgm,
1411 "firmware monitor - to debug FW (default: false - needs lots of memory)");
diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h b/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h
index 3584a75833fe..ced5ba95c23d 100644
--- a/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h
+++ b/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h
@@ -76,6 +76,7 @@
76 * &struct iwl_fw_error_dump_txcmd packets 76 * &struct iwl_fw_error_dump_txcmd packets
77 * @IWL_FW_ERROR_DUMP_DEV_FW_INFO: struct %iwl_fw_error_dump_info 77 * @IWL_FW_ERROR_DUMP_DEV_FW_INFO: struct %iwl_fw_error_dump_info
78 * info on the device / firmware. 78 * info on the device / firmware.
79 * @IWL_FW_ERROR_DUMP_FW_MONITOR: firmware monitor
79 */ 80 */
80enum iwl_fw_error_dump_type { 81enum iwl_fw_error_dump_type {
81 IWL_FW_ERROR_DUMP_SRAM = 0, 82 IWL_FW_ERROR_DUMP_SRAM = 0,
@@ -83,6 +84,7 @@ enum iwl_fw_error_dump_type {
83 IWL_FW_ERROR_DUMP_RXF = 2, 84 IWL_FW_ERROR_DUMP_RXF = 2,
84 IWL_FW_ERROR_DUMP_TXCMD = 3, 85 IWL_FW_ERROR_DUMP_TXCMD = 3,
85 IWL_FW_ERROR_DUMP_DEV_FW_INFO = 4, 86 IWL_FW_ERROR_DUMP_DEV_FW_INFO = 4,
87 IWL_FW_ERROR_DUMP_FW_MONITOR = 5,
86 88
87 IWL_FW_ERROR_DUMP_MAX, 89 IWL_FW_ERROR_DUMP_MAX,
88}; 90};
@@ -145,6 +147,22 @@ struct iwl_fw_error_dump_info {
145} __packed; 147} __packed;
146 148
147/** 149/**
150 * struct iwl_fw_error_fw_mon - FW monitor data
151 * @fw_mon_wr_ptr: the position of the write pointer in the cyclic buffer
152 * @fw_mon_base_ptr: base pointer of the data
153 * @fw_mon_cycle_cnt: number of wrap arounds
154 * @reserved: for future use
155 * @data: captured data
156 */
157struct iwl_fw_error_fw_mon {
158 __le32 fw_mon_wr_ptr;
159 __le32 fw_mon_base_ptr;
160 __le32 fw_mon_cycle_cnt;
161 __le32 reserved[3];
162 u8 data[];
163} __packed;
164
165/**
148 * iwl_fw_error_next_data - advance fw error dump data pointer 166 * iwl_fw_error_next_data - advance fw error dump data pointer
149 * @data: previous data block 167 * @data: previous data block
150 * Returns: next data block 168 * Returns: next data block
diff --git a/drivers/net/wireless/iwlwifi/iwl-modparams.h b/drivers/net/wireless/iwlwifi/iwl-modparams.h
index d051857729ab..f2d39cb011fc 100644
--- a/drivers/net/wireless/iwlwifi/iwl-modparams.h
+++ b/drivers/net/wireless/iwlwifi/iwl-modparams.h
@@ -103,6 +103,7 @@ enum iwl_disable_11n {
103 * @power_level: power level, default = 1 103 * @power_level: power level, default = 1
104 * @debug_level: levels are IWL_DL_* 104 * @debug_level: levels are IWL_DL_*
105 * @ant_coupling: antenna coupling in dB, default = 0 105 * @ant_coupling: antenna coupling in dB, default = 0
106 * @fw_monitor: allow to use firmware monitor
106 */ 107 */
107struct iwl_mod_params { 108struct iwl_mod_params {
108 int sw_crypto; 109 int sw_crypto;
@@ -120,6 +121,7 @@ struct iwl_mod_params {
120 int ant_coupling; 121 int ant_coupling;
121 char *nvm_file; 122 char *nvm_file;
122 bool uapsd_disable; 123 bool uapsd_disable;
124 bool fw_monitor;
123}; 125};
124 126
125#endif /* #__iwl_modparams_h__ */ 127#endif /* #__iwl_modparams_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-prph.h b/drivers/net/wireless/iwlwifi/iwl-prph.h
index 4997e27672b3..47033a35a402 100644
--- a/drivers/net/wireless/iwlwifi/iwl-prph.h
+++ b/drivers/net/wireless/iwlwifi/iwl-prph.h
@@ -359,4 +359,10 @@ enum secure_load_status_reg {
359#define RXF_LD_FENCE_OFFSET_ADDR (0xa00c10) 359#define RXF_LD_FENCE_OFFSET_ADDR (0xa00c10)
360#define RXF_FIFO_RD_FENCE_ADDR (0xa00c0c) 360#define RXF_FIFO_RD_FENCE_ADDR (0xa00c0c)
361 361
362/* FW monitor */
363#define MON_BUFF_BASE_ADDR (0xa03c3c)
364#define MON_BUFF_END_ADDR (0xa03c40)
365#define MON_BUFF_WRPTR (0xa03c44)
366#define MON_BUFF_CYCLE_CNT (0xa03c48)
367
362#endif /* __iwl_prph_h__ */ 368#endif /* __iwl_prph_h__ */
diff --git a/drivers/net/wireless/iwlwifi/pcie/internal.h b/drivers/net/wireless/iwlwifi/pcie/internal.h
index 6c22b23a2845..78f72c34438a 100644
--- a/drivers/net/wireless/iwlwifi/pcie/internal.h
+++ b/drivers/net/wireless/iwlwifi/pcie/internal.h
@@ -260,6 +260,9 @@ iwl_pcie_get_scratchbuf_dma(struct iwl_txq *txq, int idx)
260 * @wd_timeout: queue watchdog timeout (jiffies) 260 * @wd_timeout: queue watchdog timeout (jiffies)
261 * @reg_lock: protect hw register access 261 * @reg_lock: protect hw register access
262 * @cmd_in_flight: true when we have a host command in flight 262 * @cmd_in_flight: true when we have a host command in flight
263 * @fw_mon_phys: physical address of the buffer for the firmware monitor
264 * @fw_mon_page: points to the first page of the buffer for the firmware monitor
265 * @fw_mon_size: size of the buffer for the firmware monitor
263 */ 266 */
264struct iwl_trans_pcie { 267struct iwl_trans_pcie {
265 struct iwl_rxq rxq; 268 struct iwl_rxq rxq;
@@ -312,6 +315,10 @@ struct iwl_trans_pcie {
312 /*protect hw register */ 315 /*protect hw register */
313 spinlock_t reg_lock; 316 spinlock_t reg_lock;
314 bool cmd_in_flight; 317 bool cmd_in_flight;
318
319 dma_addr_t fw_mon_phys;
320 struct page *fw_mon_page;
321 u32 fw_mon_size;
315}; 322};
316 323
317#define IWL_TRANS_GET_PCIE_TRANS(_iwl_trans) \ 324#define IWL_TRANS_GET_PCIE_TRANS(_iwl_trans) \
diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c
index 788085bc65d7..3ffac4888c12 100644
--- a/drivers/net/wireless/iwlwifi/pcie/trans.c
+++ b/drivers/net/wireless/iwlwifi/pcie/trans.c
@@ -76,6 +76,68 @@
76#include "iwl-fw-error-dump.h" 76#include "iwl-fw-error-dump.h"
77#include "internal.h" 77#include "internal.h"
78 78
79static void iwl_pcie_free_fw_monitor(struct iwl_trans *trans)
80{
81 struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
82
83 if (!trans_pcie->fw_mon_page)
84 return;
85
86 dma_unmap_page(trans->dev, trans_pcie->fw_mon_phys,
87 trans_pcie->fw_mon_size, DMA_FROM_DEVICE);
88 __free_pages(trans_pcie->fw_mon_page,
89 get_order(trans_pcie->fw_mon_size));
90 trans_pcie->fw_mon_page = NULL;
91 trans_pcie->fw_mon_phys = 0;
92 trans_pcie->fw_mon_size = 0;
93}
94
95static void iwl_pcie_alloc_fw_monitor(struct iwl_trans *trans)
96{
97 struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
98 struct page *page;
99 dma_addr_t phys;
100 u32 size;
101 u8 power;
102
103 if (trans_pcie->fw_mon_page) {
104 dma_sync_single_for_device(trans->dev, trans_pcie->fw_mon_phys,
105 trans_pcie->fw_mon_size,
106 DMA_FROM_DEVICE);
107 return;
108 }
109
110 phys = 0;
111 for (power = 26; power >= 11; power--) {
112 int order;
113
114 size = BIT(power);
115 order = get_order(size);
116 page = alloc_pages(__GFP_COMP | __GFP_NOWARN | __GFP_ZERO,
117 order);
118 if (!page)
119 continue;
120
121 phys = dma_map_page(trans->dev, page, 0, PAGE_SIZE << order,
122 DMA_FROM_DEVICE);
123 if (dma_mapping_error(trans->dev, phys)) {
124 __free_pages(page, order);
125 continue;
126 }
127 IWL_INFO(trans,
128 "Allocated 0x%08x bytes (order %d) for firmware monitor.\n",
129 size, order);
130 break;
131 }
132
133 if (!page)
134 return;
135
136 trans_pcie->fw_mon_page = page;
137 trans_pcie->fw_mon_phys = phys;
138 trans_pcie->fw_mon_size = size;
139}
140
79static u32 iwl_trans_pcie_read_shr(struct iwl_trans *trans, u32 reg) 141static u32 iwl_trans_pcie_read_shr(struct iwl_trans *trans, u32 reg)
80{ 142{
81 iwl_write32(trans, HEEP_CTRL_WRD_PCIEX_CTRL_REG, 143 iwl_write32(trans, HEEP_CTRL_WRD_PCIEX_CTRL_REG,
@@ -675,6 +737,7 @@ static int iwl_pcie_load_cpu_sections(struct iwl_trans *trans,
675static int iwl_pcie_load_given_ucode(struct iwl_trans *trans, 737static int iwl_pcie_load_given_ucode(struct iwl_trans *trans,
676 const struct fw_img *image) 738 const struct fw_img *image)
677{ 739{
740 struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
678 int ret = 0; 741 int ret = 0;
679 int first_ucode_section; 742 int first_ucode_section;
680 743
@@ -733,6 +796,20 @@ static int iwl_pcie_load_given_ucode(struct iwl_trans *trans,
733 return ret; 796 return ret;
734 } 797 }
735 798
799 /* supported for 7000 only for the moment */
800 if (iwlwifi_mod_params.fw_monitor &&
801 trans->cfg->device_family == IWL_DEVICE_FAMILY_7000) {
802 iwl_pcie_alloc_fw_monitor(trans);
803
804 if (trans_pcie->fw_mon_size) {
805 iwl_write_prph(trans, MON_BUFF_BASE_ADDR,
806 trans_pcie->fw_mon_phys >> 4);
807 iwl_write_prph(trans, MON_BUFF_END_ADDR,
808 (trans_pcie->fw_mon_phys +
809 trans_pcie->fw_mon_size) >> 4);
810 }
811 }
812
736 /* release CPU reset */ 813 /* release CPU reset */
737 if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) 814 if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
738 iwl_write_prph(trans, RELEASE_CPU_RESET, RELEASE_CPU_RESET_BIT); 815 iwl_write_prph(trans, RELEASE_CPU_RESET, RELEASE_CPU_RESET_BIT);
@@ -1126,6 +1203,8 @@ void iwl_trans_pcie_free(struct iwl_trans *trans)
1126 if (trans_pcie->napi.poll) 1203 if (trans_pcie->napi.poll)
1127 netif_napi_del(&trans_pcie->napi); 1204 netif_napi_del(&trans_pcie->napi);
1128 1205
1206 iwl_pcie_free_fw_monitor(trans);
1207
1129 kfree(trans); 1208 kfree(trans);
1130} 1209}
1131 1210
@@ -1698,10 +1777,15 @@ static u32 iwl_trans_pcie_dump_data(struct iwl_trans *trans,
1698 u32 len; 1777 u32 len;
1699 int i, ptr; 1778 int i, ptr;
1700 1779
1780 len = sizeof(*data) +
1781 cmdq->q.n_window * (sizeof(*txcmd) + TFD_MAX_PAYLOAD_SIZE);
1782
1783 if (trans_pcie->fw_mon_page)
1784 len += sizeof(*data) + sizeof(struct iwl_fw_error_fw_mon) +
1785 trans_pcie->fw_mon_size;
1786
1701 if (!buf) 1787 if (!buf)
1702 return sizeof(*data) + 1788 return len;
1703 cmdq->q.n_window * (sizeof(*txcmd) +
1704 TFD_MAX_PAYLOAD_SIZE);
1705 1789
1706 len = 0; 1790 len = 0;
1707 data = buf; 1791 data = buf;
@@ -1729,7 +1813,40 @@ static u32 iwl_trans_pcie_dump_data(struct iwl_trans *trans,
1729 spin_unlock_bh(&cmdq->lock); 1813 spin_unlock_bh(&cmdq->lock);
1730 1814
1731 data->len = cpu_to_le32(len); 1815 data->len = cpu_to_le32(len);
1732 return sizeof(*data) + len; 1816 len += sizeof(*data);
1817
1818 if (trans_pcie->fw_mon_page) {
1819 struct iwl_fw_error_fw_mon *fw_mon_data;
1820
1821 data = iwl_fw_error_next_data(data);
1822 data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_FW_MONITOR);
1823 data->len = cpu_to_le32(trans_pcie->fw_mon_size +
1824 sizeof(*fw_mon_data));
1825 fw_mon_data = (void *)data->data;
1826 fw_mon_data->fw_mon_wr_ptr =
1827 cpu_to_le32(iwl_read_prph(trans, MON_BUFF_WRPTR));
1828 fw_mon_data->fw_mon_cycle_cnt =
1829 cpu_to_le32(iwl_read_prph(trans, MON_BUFF_CYCLE_CNT));
1830 fw_mon_data->fw_mon_base_ptr =
1831 cpu_to_le32(iwl_read_prph(trans, MON_BUFF_BASE_ADDR));
1832
1833 /*
1834 * The firmware is now asserted, it won't write anything to
1835 * the buffer. CPU can take ownership to fetch the data.
1836 * The buffer will be handed back to the device before the
1837 * firmware will be restarted.
1838 */
1839 dma_sync_single_for_cpu(trans->dev, trans_pcie->fw_mon_phys,
1840 trans_pcie->fw_mon_size,
1841 DMA_FROM_DEVICE);
1842 memcpy(fw_mon_data->data, page_address(trans_pcie->fw_mon_page),
1843 trans_pcie->fw_mon_size);
1844
1845 len += sizeof(*data) + sizeof(*fw_mon_data) +
1846 trans_pcie->fw_mon_size;
1847 }
1848
1849 return len;
1733} 1850}
1734#else 1851#else
1735static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans, 1852static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans,