diff options
author | Golan Ben-Ami <golan.ben.ami@intel.com> | 2015-10-27 13:17:14 -0400 |
---|---|---|
committer | Emmanuel Grumbach <emmanuel.grumbach@intel.com> | 2015-12-01 14:17:35 -0500 |
commit | 2f89a5d7d37706a19c2e3d338a9654bfabc5b21b (patch) | |
tree | 8da4c1a4158e104d1016556552816c31283d18fb /drivers/net | |
parent | 6f7306622ff4e6b6d59efa77383e0cd9d85f46b0 (diff) |
iwlwifi: mvm: move fw-dbg code to separate file
The fw debug functionality is big enough to warrant
a separate file. Move existing related functions to the new file.
Signed-off-by: Golan Ben-Ami <golan.ben.ami@intel.com>
Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/wireless/intel/iwlwifi/iwl-fw.h | 17 | ||||
-rw-r--r-- | drivers/net/wireless/intel/iwlwifi/mvm/Makefile | 2 | ||||
-rw-r--r-- | drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c | 1 | ||||
-rw-r--r-- | drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c | 780 | ||||
-rw-r--r-- | drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.h | 150 | ||||
-rw-r--r-- | drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 132 | ||||
-rw-r--r-- | drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c | 1 | ||||
-rw-r--r-- | drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 580 | ||||
-rw-r--r-- | drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 59 | ||||
-rw-r--r-- | drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 1 | ||||
-rw-r--r-- | drivers/net/wireless/intel/iwlwifi/mvm/rx.c | 1 | ||||
-rw-r--r-- | drivers/net/wireless/intel/iwlwifi/mvm/time-event.c | 1 | ||||
-rw-r--r-- | drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 1 | ||||
-rw-r--r-- | drivers/net/wireless/intel/iwlwifi/mvm/utils.c | 2 |
14 files changed, 941 insertions, 787 deletions
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fw.h b/drivers/net/wireless/intel/iwlwifi/iwl-fw.h index c6946f1644fc..2e1909fcd3ba 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-fw.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-fw.h | |||
@@ -305,21 +305,4 @@ iwl_fw_dbg_conf_usniffer(const struct iwl_fw *fw, u8 id) | |||
305 | return conf_tlv->usniffer; | 305 | return conf_tlv->usniffer; |
306 | } | 306 | } |
307 | 307 | ||
308 | #define iwl_fw_dbg_trigger_enabled(fw, id) ({ \ | ||
309 | void *__dbg_trigger = (fw)->dbg_trigger_tlv[(id)]; \ | ||
310 | unlikely(__dbg_trigger); \ | ||
311 | }) | ||
312 | |||
313 | static inline struct iwl_fw_dbg_trigger_tlv* | ||
314 | _iwl_fw_dbg_get_trigger(const struct iwl_fw *fw, enum iwl_fw_dbg_trigger id) | ||
315 | { | ||
316 | return fw->dbg_trigger_tlv[id]; | ||
317 | } | ||
318 | |||
319 | #define iwl_fw_dbg_get_trigger(fw, id) ({ \ | ||
320 | BUILD_BUG_ON(!__builtin_constant_p(id)); \ | ||
321 | BUILD_BUG_ON((id) >= FW_DBG_TRIGGER_MAX); \ | ||
322 | _iwl_fw_dbg_get_trigger((fw), (id)); \ | ||
323 | }) | ||
324 | |||
325 | #endif /* __iwl_fw_h__ */ | 308 | #endif /* __iwl_fw_h__ */ |
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/Makefile b/drivers/net/wireless/intel/iwlwifi/mvm/Makefile index 8c2c3d13b092..2c0d20f2a918 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/Makefile +++ b/drivers/net/wireless/intel/iwlwifi/mvm/Makefile | |||
@@ -6,7 +6,7 @@ iwlmvm-y += power.o coex.o coex_legacy.o | |||
6 | iwlmvm-y += tt.o offloading.o tdls.o | 6 | iwlmvm-y += tt.o offloading.o tdls.o |
7 | iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o debugfs-vif.o | 7 | iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o debugfs-vif.o |
8 | iwlmvm-$(CONFIG_IWLWIFI_LEDS) += led.o | 8 | iwlmvm-$(CONFIG_IWLWIFI_LEDS) += led.o |
9 | iwlmvm-y += tof.o | 9 | iwlmvm-y += tof.o fw-dbg.o |
10 | iwlmvm-$(CONFIG_PM_SLEEP) += d3.o | 10 | iwlmvm-$(CONFIG_PM_SLEEP) += d3.o |
11 | 11 | ||
12 | ccflags-y += -D__CHECK_ENDIAN__ -I$(src)/../ | 12 | ccflags-y += -D__CHECK_ENDIAN__ -I$(src)/../ |
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c index 48d5ee1b026c..3f7682da99e7 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c | |||
@@ -65,6 +65,7 @@ | |||
65 | #include <linux/vmalloc.h> | 65 | #include <linux/vmalloc.h> |
66 | 66 | ||
67 | #include "mvm.h" | 67 | #include "mvm.h" |
68 | #include "fw-dbg.h" | ||
68 | #include "sta.h" | 69 | #include "sta.h" |
69 | #include "iwl-io.h" | 70 | #include "iwl-io.h" |
70 | #include "debugfs.h" | 71 | #include "debugfs.h" |
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c new file mode 100644 index 000000000000..b8e0591970ae --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c | |||
@@ -0,0 +1,780 @@ | |||
1 | /****************************************************************************** | ||
2 | * | ||
3 | * This file is provided under a dual BSD/GPLv2 license. When using or | ||
4 | * redistributing this file, you may do so under either license. | ||
5 | * | ||
6 | * GPL LICENSE SUMMARY | ||
7 | * | ||
8 | * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. | ||
9 | * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH | ||
10 | * Copyright(c) 2015 Intel Deutschland GmbH | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of version 2 of the GNU General Public License as | ||
14 | * published by the Free Software Foundation. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, but | ||
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
19 | * General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; | ||
23 | * | ||
24 | * The full GNU General Public License is included in this distribution | ||
25 | * in the file called COPYING. | ||
26 | * | ||
27 | * Contact Information: | ||
28 | * Intel Linux Wireless <ilw@linux.intel.com> | ||
29 | * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 | ||
30 | * | ||
31 | * BSD LICENSE | ||
32 | * | ||
33 | * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. | ||
34 | * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH | ||
35 | * Copyright(c) 2015 Intel Deutschland GmbH | ||
36 | * All rights reserved. | ||
37 | * | ||
38 | * Redistribution and use in source and binary forms, with or without | ||
39 | * modification, are permitted provided that the following conditions | ||
40 | * are met: | ||
41 | * | ||
42 | * * Redistributions of source code must retain the above copyright | ||
43 | * notice, this list of conditions and the following disclaimer. | ||
44 | * * Redistributions in binary form must reproduce the above copyright | ||
45 | * notice, this list of conditions and the following disclaimer in | ||
46 | * the documentation and/or other materials provided with the | ||
47 | * distribution. | ||
48 | * * Neither the name Intel Corporation nor the names of its | ||
49 | * contributors may be used to endorse or promote products derived | ||
50 | * from this software without specific prior written permission. | ||
51 | * | ||
52 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
53 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
54 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
55 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
56 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
57 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
58 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
59 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
60 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
61 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
62 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
63 | * | ||
64 | *****************************************************************************/ | ||
65 | #include <linux/devcoredump.h> | ||
66 | |||
67 | #include "fw-dbg.h" | ||
68 | #include "iwl-io.h" | ||
69 | #include "mvm.h" | ||
70 | #include "iwl-prph.h" | ||
71 | #include "iwl-csr.h" | ||
72 | |||
73 | static ssize_t iwl_mvm_read_coredump(char *buffer, loff_t offset, size_t count, | ||
74 | const void *data, size_t datalen) | ||
75 | { | ||
76 | const struct iwl_mvm_dump_ptrs *dump_ptrs = data; | ||
77 | ssize_t bytes_read; | ||
78 | ssize_t bytes_read_trans; | ||
79 | |||
80 | if (offset < dump_ptrs->op_mode_len) { | ||
81 | bytes_read = min_t(ssize_t, count, | ||
82 | dump_ptrs->op_mode_len - offset); | ||
83 | memcpy(buffer, (u8 *)dump_ptrs->op_mode_ptr + offset, | ||
84 | bytes_read); | ||
85 | offset += bytes_read; | ||
86 | count -= bytes_read; | ||
87 | |||
88 | if (count == 0) | ||
89 | return bytes_read; | ||
90 | } else { | ||
91 | bytes_read = 0; | ||
92 | } | ||
93 | |||
94 | if (!dump_ptrs->trans_ptr) | ||
95 | return bytes_read; | ||
96 | |||
97 | offset -= dump_ptrs->op_mode_len; | ||
98 | bytes_read_trans = min_t(ssize_t, count, | ||
99 | dump_ptrs->trans_ptr->len - offset); | ||
100 | memcpy(buffer + bytes_read, | ||
101 | (u8 *)dump_ptrs->trans_ptr->data + offset, | ||
102 | bytes_read_trans); | ||
103 | |||
104 | return bytes_read + bytes_read_trans; | ||
105 | } | ||
106 | |||
107 | static void iwl_mvm_free_coredump(const void *data) | ||
108 | { | ||
109 | const struct iwl_mvm_dump_ptrs *fw_error_dump = data; | ||
110 | |||
111 | vfree(fw_error_dump->op_mode_ptr); | ||
112 | vfree(fw_error_dump->trans_ptr); | ||
113 | kfree(fw_error_dump); | ||
114 | } | ||
115 | |||
116 | static void iwl_mvm_dump_fifos(struct iwl_mvm *mvm, | ||
117 | struct iwl_fw_error_dump_data **dump_data) | ||
118 | { | ||
119 | struct iwl_fw_error_dump_fifo *fifo_hdr; | ||
120 | u32 *fifo_data; | ||
121 | u32 fifo_len; | ||
122 | unsigned long flags; | ||
123 | int i, j; | ||
124 | |||
125 | if (!iwl_trans_grab_nic_access(mvm->trans, false, &flags)) | ||
126 | return; | ||
127 | |||
128 | /* Pull RXF data from all RXFs */ | ||
129 | for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.rxfifo_size); i++) { | ||
130 | /* | ||
131 | * Keep aside the additional offset that might be needed for | ||
132 | * next RXF | ||
133 | */ | ||
134 | u32 offset_diff = RXF_DIFF_FROM_PREV * i; | ||
135 | |||
136 | fifo_hdr = (void *)(*dump_data)->data; | ||
137 | fifo_data = (void *)fifo_hdr->data; | ||
138 | fifo_len = mvm->shared_mem_cfg.rxfifo_size[i]; | ||
139 | |||
140 | /* No need to try to read the data if the length is 0 */ | ||
141 | if (fifo_len == 0) | ||
142 | continue; | ||
143 | |||
144 | /* Add a TLV for the RXF */ | ||
145 | (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RXF); | ||
146 | (*dump_data)->len = cpu_to_le32(fifo_len + sizeof(*fifo_hdr)); | ||
147 | |||
148 | fifo_hdr->fifo_num = cpu_to_le32(i); | ||
149 | fifo_hdr->available_bytes = | ||
150 | cpu_to_le32(iwl_trans_read_prph(mvm->trans, | ||
151 | RXF_RD_D_SPACE + | ||
152 | offset_diff)); | ||
153 | fifo_hdr->wr_ptr = | ||
154 | cpu_to_le32(iwl_trans_read_prph(mvm->trans, | ||
155 | RXF_RD_WR_PTR + | ||
156 | offset_diff)); | ||
157 | fifo_hdr->rd_ptr = | ||
158 | cpu_to_le32(iwl_trans_read_prph(mvm->trans, | ||
159 | RXF_RD_RD_PTR + | ||
160 | offset_diff)); | ||
161 | fifo_hdr->fence_ptr = | ||
162 | cpu_to_le32(iwl_trans_read_prph(mvm->trans, | ||
163 | RXF_RD_FENCE_PTR + | ||
164 | offset_diff)); | ||
165 | fifo_hdr->fence_mode = | ||
166 | cpu_to_le32(iwl_trans_read_prph(mvm->trans, | ||
167 | RXF_SET_FENCE_MODE + | ||
168 | offset_diff)); | ||
169 | |||
170 | /* Lock fence */ | ||
171 | iwl_trans_write_prph(mvm->trans, | ||
172 | RXF_SET_FENCE_MODE + offset_diff, 0x1); | ||
173 | /* Set fence pointer to the same place like WR pointer */ | ||
174 | iwl_trans_write_prph(mvm->trans, | ||
175 | RXF_LD_WR2FENCE + offset_diff, 0x1); | ||
176 | /* Set fence offset */ | ||
177 | iwl_trans_write_prph(mvm->trans, | ||
178 | RXF_LD_FENCE_OFFSET_ADDR + offset_diff, | ||
179 | 0x0); | ||
180 | |||
181 | /* Read FIFO */ | ||
182 | fifo_len /= sizeof(u32); /* Size in DWORDS */ | ||
183 | for (j = 0; j < fifo_len; j++) | ||
184 | fifo_data[j] = iwl_trans_read_prph(mvm->trans, | ||
185 | RXF_FIFO_RD_FENCE_INC + | ||
186 | offset_diff); | ||
187 | *dump_data = iwl_fw_error_next_data(*dump_data); | ||
188 | } | ||
189 | |||
190 | /* Pull TXF data from all TXFs */ | ||
191 | for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.txfifo_size); i++) { | ||
192 | /* Mark the number of TXF we're pulling now */ | ||
193 | iwl_trans_write_prph(mvm->trans, TXF_LARC_NUM, i); | ||
194 | |||
195 | fifo_hdr = (void *)(*dump_data)->data; | ||
196 | fifo_data = (void *)fifo_hdr->data; | ||
197 | fifo_len = mvm->shared_mem_cfg.txfifo_size[i]; | ||
198 | |||
199 | /* No need to try to read the data if the length is 0 */ | ||
200 | if (fifo_len == 0) | ||
201 | continue; | ||
202 | |||
203 | /* Add a TLV for the FIFO */ | ||
204 | (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_TXF); | ||
205 | (*dump_data)->len = cpu_to_le32(fifo_len + sizeof(*fifo_hdr)); | ||
206 | |||
207 | fifo_hdr->fifo_num = cpu_to_le32(i); | ||
208 | fifo_hdr->available_bytes = | ||
209 | cpu_to_le32(iwl_trans_read_prph(mvm->trans, | ||
210 | TXF_FIFO_ITEM_CNT)); | ||
211 | fifo_hdr->wr_ptr = | ||
212 | cpu_to_le32(iwl_trans_read_prph(mvm->trans, | ||
213 | TXF_WR_PTR)); | ||
214 | fifo_hdr->rd_ptr = | ||
215 | cpu_to_le32(iwl_trans_read_prph(mvm->trans, | ||
216 | TXF_RD_PTR)); | ||
217 | fifo_hdr->fence_ptr = | ||
218 | cpu_to_le32(iwl_trans_read_prph(mvm->trans, | ||
219 | TXF_FENCE_PTR)); | ||
220 | fifo_hdr->fence_mode = | ||
221 | cpu_to_le32(iwl_trans_read_prph(mvm->trans, | ||
222 | TXF_LOCK_FENCE)); | ||
223 | |||
224 | /* Set the TXF_READ_MODIFY_ADDR to TXF_WR_PTR */ | ||
225 | iwl_trans_write_prph(mvm->trans, TXF_READ_MODIFY_ADDR, | ||
226 | TXF_WR_PTR); | ||
227 | |||
228 | /* Dummy-read to advance the read pointer to the head */ | ||
229 | iwl_trans_read_prph(mvm->trans, TXF_READ_MODIFY_DATA); | ||
230 | |||
231 | /* Read FIFO */ | ||
232 | fifo_len /= sizeof(u32); /* Size in DWORDS */ | ||
233 | for (j = 0; j < fifo_len; j++) | ||
234 | fifo_data[j] = iwl_trans_read_prph(mvm->trans, | ||
235 | TXF_READ_MODIFY_DATA); | ||
236 | *dump_data = iwl_fw_error_next_data(*dump_data); | ||
237 | } | ||
238 | |||
239 | iwl_trans_release_nic_access(mvm->trans, &flags); | ||
240 | } | ||
241 | |||
242 | void iwl_mvm_free_fw_dump_desc(struct iwl_mvm *mvm) | ||
243 | { | ||
244 | if (mvm->fw_dump_desc == &iwl_mvm_dump_desc_assert || | ||
245 | !mvm->fw_dump_desc) | ||
246 | return; | ||
247 | |||
248 | kfree(mvm->fw_dump_desc); | ||
249 | mvm->fw_dump_desc = NULL; | ||
250 | } | ||
251 | |||
252 | #define IWL8260_ICCM_OFFSET 0x44000 /* Only for B-step */ | ||
253 | #define IWL8260_ICCM_LEN 0xC000 /* Only for B-step */ | ||
254 | |||
255 | static const struct { | ||
256 | u32 start, end; | ||
257 | } iwl_prph_dump_addr[] = { | ||
258 | { .start = 0x00a00000, .end = 0x00a00000 }, | ||
259 | { .start = 0x00a0000c, .end = 0x00a00024 }, | ||
260 | { .start = 0x00a0002c, .end = 0x00a0003c }, | ||
261 | { .start = 0x00a00410, .end = 0x00a00418 }, | ||
262 | { .start = 0x00a00420, .end = 0x00a00420 }, | ||
263 | { .start = 0x00a00428, .end = 0x00a00428 }, | ||
264 | { .start = 0x00a00430, .end = 0x00a0043c }, | ||
265 | { .start = 0x00a00444, .end = 0x00a00444 }, | ||
266 | { .start = 0x00a004c0, .end = 0x00a004cc }, | ||
267 | { .start = 0x00a004d8, .end = 0x00a004d8 }, | ||
268 | { .start = 0x00a004e0, .end = 0x00a004f0 }, | ||
269 | { .start = 0x00a00840, .end = 0x00a00840 }, | ||
270 | { .start = 0x00a00850, .end = 0x00a00858 }, | ||
271 | { .start = 0x00a01004, .end = 0x00a01008 }, | ||
272 | { .start = 0x00a01010, .end = 0x00a01010 }, | ||
273 | { .start = 0x00a01018, .end = 0x00a01018 }, | ||
274 | { .start = 0x00a01024, .end = 0x00a01024 }, | ||
275 | { .start = 0x00a0102c, .end = 0x00a01034 }, | ||
276 | { .start = 0x00a0103c, .end = 0x00a01040 }, | ||
277 | { .start = 0x00a01048, .end = 0x00a01094 }, | ||
278 | { .start = 0x00a01c00, .end = 0x00a01c20 }, | ||
279 | { .start = 0x00a01c58, .end = 0x00a01c58 }, | ||
280 | { .start = 0x00a01c7c, .end = 0x00a01c7c }, | ||
281 | { .start = 0x00a01c28, .end = 0x00a01c54 }, | ||
282 | { .start = 0x00a01c5c, .end = 0x00a01c5c }, | ||
283 | { .start = 0x00a01c60, .end = 0x00a01cdc }, | ||
284 | { .start = 0x00a01ce0, .end = 0x00a01d0c }, | ||
285 | { .start = 0x00a01d18, .end = 0x00a01d20 }, | ||
286 | { .start = 0x00a01d2c, .end = 0x00a01d30 }, | ||
287 | { .start = 0x00a01d40, .end = 0x00a01d5c }, | ||
288 | { .start = 0x00a01d80, .end = 0x00a01d80 }, | ||
289 | { .start = 0x00a01d98, .end = 0x00a01d9c }, | ||
290 | { .start = 0x00a01da8, .end = 0x00a01da8 }, | ||
291 | { .start = 0x00a01db8, .end = 0x00a01df4 }, | ||
292 | { .start = 0x00a01dc0, .end = 0x00a01dfc }, | ||
293 | { .start = 0x00a01e00, .end = 0x00a01e2c }, | ||
294 | { .start = 0x00a01e40, .end = 0x00a01e60 }, | ||
295 | { .start = 0x00a01e68, .end = 0x00a01e6c }, | ||
296 | { .start = 0x00a01e74, .end = 0x00a01e74 }, | ||
297 | { .start = 0x00a01e84, .end = 0x00a01e90 }, | ||
298 | { .start = 0x00a01e9c, .end = 0x00a01ec4 }, | ||
299 | { .start = 0x00a01ed0, .end = 0x00a01ee0 }, | ||
300 | { .start = 0x00a01f00, .end = 0x00a01f1c }, | ||
301 | { .start = 0x00a01f44, .end = 0x00a01ffc }, | ||
302 | { .start = 0x00a02000, .end = 0x00a02048 }, | ||
303 | { .start = 0x00a02068, .end = 0x00a020f0 }, | ||
304 | { .start = 0x00a02100, .end = 0x00a02118 }, | ||
305 | { .start = 0x00a02140, .end = 0x00a0214c }, | ||
306 | { .start = 0x00a02168, .end = 0x00a0218c }, | ||
307 | { .start = 0x00a021c0, .end = 0x00a021c0 }, | ||
308 | { .start = 0x00a02400, .end = 0x00a02410 }, | ||
309 | { .start = 0x00a02418, .end = 0x00a02420 }, | ||
310 | { .start = 0x00a02428, .end = 0x00a0242c }, | ||
311 | { .start = 0x00a02434, .end = 0x00a02434 }, | ||
312 | { .start = 0x00a02440, .end = 0x00a02460 }, | ||
313 | { .start = 0x00a02468, .end = 0x00a024b0 }, | ||
314 | { .start = 0x00a024c8, .end = 0x00a024cc }, | ||
315 | { .start = 0x00a02500, .end = 0x00a02504 }, | ||
316 | { .start = 0x00a0250c, .end = 0x00a02510 }, | ||
317 | { .start = 0x00a02540, .end = 0x00a02554 }, | ||
318 | { .start = 0x00a02580, .end = 0x00a025f4 }, | ||
319 | { .start = 0x00a02600, .end = 0x00a0260c }, | ||
320 | { .start = 0x00a02648, .end = 0x00a02650 }, | ||
321 | { .start = 0x00a02680, .end = 0x00a02680 }, | ||
322 | { .start = 0x00a026c0, .end = 0x00a026d0 }, | ||
323 | { .start = 0x00a02700, .end = 0x00a0270c }, | ||
324 | { .start = 0x00a02804, .end = 0x00a02804 }, | ||
325 | { .start = 0x00a02818, .end = 0x00a0281c }, | ||
326 | { .start = 0x00a02c00, .end = 0x00a02db4 }, | ||
327 | { .start = 0x00a02df4, .end = 0x00a02fb0 }, | ||
328 | { .start = 0x00a03000, .end = 0x00a03014 }, | ||
329 | { .start = 0x00a0301c, .end = 0x00a0302c }, | ||
330 | { .start = 0x00a03034, .end = 0x00a03038 }, | ||
331 | { .start = 0x00a03040, .end = 0x00a03048 }, | ||
332 | { .start = 0x00a03060, .end = 0x00a03068 }, | ||
333 | { .start = 0x00a03070, .end = 0x00a03074 }, | ||
334 | { .start = 0x00a0307c, .end = 0x00a0307c }, | ||
335 | { .start = 0x00a03080, .end = 0x00a03084 }, | ||
336 | { .start = 0x00a0308c, .end = 0x00a03090 }, | ||
337 | { .start = 0x00a03098, .end = 0x00a03098 }, | ||
338 | { .start = 0x00a030a0, .end = 0x00a030a0 }, | ||
339 | { .start = 0x00a030a8, .end = 0x00a030b4 }, | ||
340 | { .start = 0x00a030bc, .end = 0x00a030bc }, | ||
341 | { .start = 0x00a030c0, .end = 0x00a0312c }, | ||
342 | { .start = 0x00a03c00, .end = 0x00a03c5c }, | ||
343 | { .start = 0x00a04400, .end = 0x00a04454 }, | ||
344 | { .start = 0x00a04460, .end = 0x00a04474 }, | ||
345 | { .start = 0x00a044c0, .end = 0x00a044ec }, | ||
346 | { .start = 0x00a04500, .end = 0x00a04504 }, | ||
347 | { .start = 0x00a04510, .end = 0x00a04538 }, | ||
348 | { .start = 0x00a04540, .end = 0x00a04548 }, | ||
349 | { .start = 0x00a04560, .end = 0x00a0457c }, | ||
350 | { .start = 0x00a04590, .end = 0x00a04598 }, | ||
351 | { .start = 0x00a045c0, .end = 0x00a045f4 }, | ||
352 | }; | ||
353 | |||
354 | static u32 iwl_dump_prph(struct iwl_trans *trans, | ||
355 | struct iwl_fw_error_dump_data **data) | ||
356 | { | ||
357 | struct iwl_fw_error_dump_prph *prph; | ||
358 | unsigned long flags; | ||
359 | u32 prph_len = 0, i; | ||
360 | |||
361 | if (!iwl_trans_grab_nic_access(trans, false, &flags)) | ||
362 | return 0; | ||
363 | |||
364 | for (i = 0; i < ARRAY_SIZE(iwl_prph_dump_addr); i++) { | ||
365 | /* The range includes both boundaries */ | ||
366 | int num_bytes_in_chunk = iwl_prph_dump_addr[i].end - | ||
367 | iwl_prph_dump_addr[i].start + 4; | ||
368 | int reg; | ||
369 | __le32 *val; | ||
370 | |||
371 | prph_len += sizeof(**data) + sizeof(*prph) + num_bytes_in_chunk; | ||
372 | |||
373 | (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PRPH); | ||
374 | (*data)->len = cpu_to_le32(sizeof(*prph) + | ||
375 | num_bytes_in_chunk); | ||
376 | prph = (void *)(*data)->data; | ||
377 | prph->prph_start = cpu_to_le32(iwl_prph_dump_addr[i].start); | ||
378 | val = (void *)prph->data; | ||
379 | |||
380 | for (reg = iwl_prph_dump_addr[i].start; | ||
381 | reg <= iwl_prph_dump_addr[i].end; | ||
382 | reg += 4) | ||
383 | *val++ = cpu_to_le32(iwl_read_prph_no_grab(trans, | ||
384 | reg)); | ||
385 | |||
386 | *data = iwl_fw_error_next_data(*data); | ||
387 | } | ||
388 | |||
389 | iwl_trans_release_nic_access(trans, &flags); | ||
390 | |||
391 | return prph_len; | ||
392 | } | ||
393 | |||
394 | void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) | ||
395 | { | ||
396 | struct iwl_fw_error_dump_file *dump_file; | ||
397 | struct iwl_fw_error_dump_data *dump_data; | ||
398 | struct iwl_fw_error_dump_info *dump_info; | ||
399 | struct iwl_fw_error_dump_mem *dump_mem; | ||
400 | struct iwl_fw_error_dump_trigger_desc *dump_trig; | ||
401 | struct iwl_mvm_dump_ptrs *fw_error_dump; | ||
402 | u32 sram_len, sram_ofs; | ||
403 | u32 file_len, fifo_data_len = 0; | ||
404 | u32 smem_len = mvm->cfg->smem_len; | ||
405 | u32 sram2_len = mvm->cfg->dccm2_len; | ||
406 | bool monitor_dump_only = false; | ||
407 | int i; | ||
408 | |||
409 | lockdep_assert_held(&mvm->mutex); | ||
410 | |||
411 | /* there's no point in fw dump if the bus is dead */ | ||
412 | if (test_bit(STATUS_TRANS_DEAD, &mvm->trans->status)) { | ||
413 | IWL_ERR(mvm, "Skip fw error dump since bus is dead\n"); | ||
414 | return; | ||
415 | } | ||
416 | |||
417 | if (mvm->fw_dump_trig && | ||
418 | mvm->fw_dump_trig->mode & IWL_FW_DBG_TRIGGER_MONITOR_ONLY) | ||
419 | monitor_dump_only = true; | ||
420 | |||
421 | fw_error_dump = kzalloc(sizeof(*fw_error_dump), GFP_KERNEL); | ||
422 | if (!fw_error_dump) | ||
423 | return; | ||
424 | |||
425 | /* SRAM - include stack CCM if driver knows the values for it */ | ||
426 | if (!mvm->cfg->dccm_offset || !mvm->cfg->dccm_len) { | ||
427 | const struct fw_img *img; | ||
428 | |||
429 | img = &mvm->fw->img[mvm->cur_ucode]; | ||
430 | sram_ofs = img->sec[IWL_UCODE_SECTION_DATA].offset; | ||
431 | sram_len = img->sec[IWL_UCODE_SECTION_DATA].len; | ||
432 | } else { | ||
433 | sram_ofs = mvm->cfg->dccm_offset; | ||
434 | sram_len = mvm->cfg->dccm_len; | ||
435 | } | ||
436 | |||
437 | /* reading RXF/TXF sizes */ | ||
438 | if (test_bit(STATUS_FW_ERROR, &mvm->trans->status)) { | ||
439 | struct iwl_mvm_shared_mem_cfg *mem_cfg = &mvm->shared_mem_cfg; | ||
440 | |||
441 | fifo_data_len = 0; | ||
442 | |||
443 | /* Count RXF size */ | ||
444 | for (i = 0; i < ARRAY_SIZE(mem_cfg->rxfifo_size); i++) { | ||
445 | if (!mem_cfg->rxfifo_size[i]) | ||
446 | continue; | ||
447 | |||
448 | /* Add header info */ | ||
449 | fifo_data_len += mem_cfg->rxfifo_size[i] + | ||
450 | sizeof(*dump_data) + | ||
451 | sizeof(struct iwl_fw_error_dump_fifo); | ||
452 | } | ||
453 | |||
454 | for (i = 0; i < ARRAY_SIZE(mem_cfg->txfifo_size); i++) { | ||
455 | if (!mem_cfg->txfifo_size[i]) | ||
456 | continue; | ||
457 | |||
458 | /* Add header info */ | ||
459 | fifo_data_len += mem_cfg->txfifo_size[i] + | ||
460 | sizeof(*dump_data) + | ||
461 | sizeof(struct iwl_fw_error_dump_fifo); | ||
462 | } | ||
463 | } | ||
464 | |||
465 | file_len = sizeof(*dump_file) + | ||
466 | sizeof(*dump_data) * 2 + | ||
467 | sram_len + sizeof(*dump_mem) + | ||
468 | fifo_data_len + | ||
469 | sizeof(*dump_info); | ||
470 | |||
471 | /* Make room for the SMEM, if it exists */ | ||
472 | if (smem_len) | ||
473 | file_len += sizeof(*dump_data) + sizeof(*dump_mem) + smem_len; | ||
474 | |||
475 | /* Make room for the secondary SRAM, if it exists */ | ||
476 | if (sram2_len) | ||
477 | file_len += sizeof(*dump_data) + sizeof(*dump_mem) + sram2_len; | ||
478 | |||
479 | /* Make room for fw's virtual image pages, if it exists */ | ||
480 | if (mvm->fw->img[mvm->cur_ucode].paging_mem_size) | ||
481 | file_len += mvm->num_of_paging_blk * | ||
482 | (sizeof(*dump_data) + | ||
483 | sizeof(struct iwl_fw_error_dump_paging) + | ||
484 | PAGING_BLOCK_SIZE); | ||
485 | |||
486 | /* If we only want a monitor dump, reset the file length */ | ||
487 | if (monitor_dump_only) { | ||
488 | file_len = sizeof(*dump_file) + sizeof(*dump_data) + | ||
489 | sizeof(*dump_info); | ||
490 | } | ||
491 | |||
492 | /* Make room for PRPH registers */ | ||
493 | for (i = 0; i < ARRAY_SIZE(iwl_prph_dump_addr); i++) { | ||
494 | /* The range includes both boundaries */ | ||
495 | int num_bytes_in_chunk = iwl_prph_dump_addr[i].end - | ||
496 | iwl_prph_dump_addr[i].start + 4; | ||
497 | |||
498 | file_len += sizeof(*dump_data) + | ||
499 | sizeof(struct iwl_fw_error_dump_prph) + | ||
500 | num_bytes_in_chunk; | ||
501 | } | ||
502 | |||
503 | /* | ||
504 | * In 8000 HW family B-step include the ICCM (which resides separately) | ||
505 | */ | ||
506 | if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000 && | ||
507 | CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_B_STEP) | ||
508 | file_len += sizeof(*dump_data) + sizeof(*dump_mem) + | ||
509 | IWL8260_ICCM_LEN; | ||
510 | |||
511 | if (mvm->fw_dump_desc) | ||
512 | file_len += sizeof(*dump_data) + sizeof(*dump_trig) + | ||
513 | mvm->fw_dump_desc->len; | ||
514 | |||
515 | dump_file = vzalloc(file_len); | ||
516 | if (!dump_file) { | ||
517 | kfree(fw_error_dump); | ||
518 | iwl_mvm_free_fw_dump_desc(mvm); | ||
519 | return; | ||
520 | } | ||
521 | |||
522 | fw_error_dump->op_mode_ptr = dump_file; | ||
523 | |||
524 | dump_file->barker = cpu_to_le32(IWL_FW_ERROR_DUMP_BARKER); | ||
525 | dump_data = (void *)dump_file->data; | ||
526 | |||
527 | dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_DEV_FW_INFO); | ||
528 | dump_data->len = cpu_to_le32(sizeof(*dump_info)); | ||
529 | dump_info = (void *)dump_data->data; | ||
530 | dump_info->device_family = | ||
531 | mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000 ? | ||
532 | cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_7) : | ||
533 | cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_8); | ||
534 | dump_info->hw_step = cpu_to_le32(CSR_HW_REV_STEP(mvm->trans->hw_rev)); | ||
535 | memcpy(dump_info->fw_human_readable, mvm->fw->human_readable, | ||
536 | sizeof(dump_info->fw_human_readable)); | ||
537 | strncpy(dump_info->dev_human_readable, mvm->cfg->name, | ||
538 | sizeof(dump_info->dev_human_readable)); | ||
539 | strncpy(dump_info->bus_human_readable, mvm->dev->bus->name, | ||
540 | sizeof(dump_info->bus_human_readable)); | ||
541 | |||
542 | dump_data = iwl_fw_error_next_data(dump_data); | ||
543 | /* We only dump the FIFOs if the FW is in error state */ | ||
544 | if (test_bit(STATUS_FW_ERROR, &mvm->trans->status)) | ||
545 | iwl_mvm_dump_fifos(mvm, &dump_data); | ||
546 | |||
547 | if (mvm->fw_dump_desc) { | ||
548 | dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_ERROR_INFO); | ||
549 | dump_data->len = cpu_to_le32(sizeof(*dump_trig) + | ||
550 | mvm->fw_dump_desc->len); | ||
551 | dump_trig = (void *)dump_data->data; | ||
552 | memcpy(dump_trig, &mvm->fw_dump_desc->trig_desc, | ||
553 | sizeof(*dump_trig) + mvm->fw_dump_desc->len); | ||
554 | |||
555 | /* now we can free this copy */ | ||
556 | iwl_mvm_free_fw_dump_desc(mvm); | ||
557 | dump_data = iwl_fw_error_next_data(dump_data); | ||
558 | } | ||
559 | |||
560 | /* In case we only want monitor dump, skip to dump trasport data */ | ||
561 | if (monitor_dump_only) | ||
562 | goto dump_trans_data; | ||
563 | |||
564 | dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); | ||
565 | dump_data->len = cpu_to_le32(sram_len + sizeof(*dump_mem)); | ||
566 | dump_mem = (void *)dump_data->data; | ||
567 | dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM); | ||
568 | dump_mem->offset = cpu_to_le32(sram_ofs); | ||
569 | iwl_trans_read_mem_bytes(mvm->trans, sram_ofs, dump_mem->data, | ||
570 | sram_len); | ||
571 | |||
572 | if (smem_len) { | ||
573 | dump_data = iwl_fw_error_next_data(dump_data); | ||
574 | dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); | ||
575 | dump_data->len = cpu_to_le32(smem_len + sizeof(*dump_mem)); | ||
576 | dump_mem = (void *)dump_data->data; | ||
577 | dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SMEM); | ||
578 | dump_mem->offset = cpu_to_le32(mvm->cfg->smem_offset); | ||
579 | iwl_trans_read_mem_bytes(mvm->trans, mvm->cfg->smem_offset, | ||
580 | dump_mem->data, smem_len); | ||
581 | } | ||
582 | |||
583 | if (sram2_len) { | ||
584 | dump_data = iwl_fw_error_next_data(dump_data); | ||
585 | dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); | ||
586 | dump_data->len = cpu_to_le32(sram2_len + sizeof(*dump_mem)); | ||
587 | dump_mem = (void *)dump_data->data; | ||
588 | dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM); | ||
589 | dump_mem->offset = cpu_to_le32(mvm->cfg->dccm2_offset); | ||
590 | iwl_trans_read_mem_bytes(mvm->trans, mvm->cfg->dccm2_offset, | ||
591 | dump_mem->data, sram2_len); | ||
592 | } | ||
593 | |||
594 | if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000 && | ||
595 | CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_B_STEP) { | ||
596 | dump_data = iwl_fw_error_next_data(dump_data); | ||
597 | dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); | ||
598 | dump_data->len = cpu_to_le32(IWL8260_ICCM_LEN + | ||
599 | sizeof(*dump_mem)); | ||
600 | dump_mem = (void *)dump_data->data; | ||
601 | dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM); | ||
602 | dump_mem->offset = cpu_to_le32(IWL8260_ICCM_OFFSET); | ||
603 | iwl_trans_read_mem_bytes(mvm->trans, IWL8260_ICCM_OFFSET, | ||
604 | dump_mem->data, IWL8260_ICCM_LEN); | ||
605 | } | ||
606 | |||
607 | /* Dump fw's virtual image */ | ||
608 | if (mvm->fw->img[mvm->cur_ucode].paging_mem_size) { | ||
609 | u32 i; | ||
610 | |||
611 | for (i = 1; i < mvm->num_of_paging_blk + 1; i++) { | ||
612 | struct iwl_fw_error_dump_paging *paging; | ||
613 | struct page *pages = | ||
614 | mvm->fw_paging_db[i].fw_paging_block; | ||
615 | |||
616 | dump_data = iwl_fw_error_next_data(dump_data); | ||
617 | dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PAGING); | ||
618 | dump_data->len = cpu_to_le32(sizeof(*paging) + | ||
619 | PAGING_BLOCK_SIZE); | ||
620 | paging = (void *)dump_data->data; | ||
621 | paging->index = cpu_to_le32(i); | ||
622 | memcpy(paging->data, page_address(pages), | ||
623 | PAGING_BLOCK_SIZE); | ||
624 | } | ||
625 | } | ||
626 | |||
627 | dump_data = iwl_fw_error_next_data(dump_data); | ||
628 | iwl_dump_prph(mvm->trans, &dump_data); | ||
629 | |||
630 | dump_trans_data: | ||
631 | fw_error_dump->trans_ptr = iwl_trans_dump_data(mvm->trans, | ||
632 | mvm->fw_dump_trig); | ||
633 | fw_error_dump->op_mode_len = file_len; | ||
634 | if (fw_error_dump->trans_ptr) | ||
635 | file_len += fw_error_dump->trans_ptr->len; | ||
636 | dump_file->file_len = cpu_to_le32(file_len); | ||
637 | |||
638 | dev_coredumpm(mvm->trans->dev, THIS_MODULE, fw_error_dump, 0, | ||
639 | GFP_KERNEL, iwl_mvm_read_coredump, iwl_mvm_free_coredump); | ||
640 | |||
641 | mvm->fw_dump_trig = NULL; | ||
642 | clear_bit(IWL_MVM_STATUS_DUMPING_FW_LOG, &mvm->status); | ||
643 | } | ||
644 | |||
645 | struct iwl_mvm_dump_desc iwl_mvm_dump_desc_assert = { | ||
646 | .trig_desc = { | ||
647 | .type = cpu_to_le32(FW_DBG_TRIGGER_FW_ASSERT), | ||
648 | }, | ||
649 | }; | ||
650 | |||
651 | int iwl_mvm_fw_dbg_collect_desc(struct iwl_mvm *mvm, | ||
652 | struct iwl_mvm_dump_desc *desc, | ||
653 | struct iwl_fw_dbg_trigger_tlv *trigger) | ||
654 | { | ||
655 | unsigned int delay = 0; | ||
656 | |||
657 | if (trigger) | ||
658 | delay = msecs_to_jiffies(le32_to_cpu(trigger->stop_delay)); | ||
659 | |||
660 | if (test_and_set_bit(IWL_MVM_STATUS_DUMPING_FW_LOG, &mvm->status)) | ||
661 | return -EBUSY; | ||
662 | |||
663 | if (WARN_ON(mvm->fw_dump_desc)) | ||
664 | iwl_mvm_free_fw_dump_desc(mvm); | ||
665 | |||
666 | IWL_WARN(mvm, "Collecting data: trigger %d fired.\n", | ||
667 | le32_to_cpu(desc->trig_desc.type)); | ||
668 | |||
669 | mvm->fw_dump_desc = desc; | ||
670 | mvm->fw_dump_trig = trigger; | ||
671 | |||
672 | queue_delayed_work(system_wq, &mvm->fw_dump_wk, delay); | ||
673 | |||
674 | return 0; | ||
675 | } | ||
676 | |||
677 | int iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm, enum iwl_fw_dbg_trigger trig, | ||
678 | const char *str, size_t len, | ||
679 | struct iwl_fw_dbg_trigger_tlv *trigger) | ||
680 | { | ||
681 | struct iwl_mvm_dump_desc *desc; | ||
682 | |||
683 | desc = kzalloc(sizeof(*desc) + len, GFP_ATOMIC); | ||
684 | if (!desc) | ||
685 | return -ENOMEM; | ||
686 | |||
687 | desc->len = len; | ||
688 | desc->trig_desc.type = cpu_to_le32(trig); | ||
689 | memcpy(desc->trig_desc.data, str, len); | ||
690 | |||
691 | return iwl_mvm_fw_dbg_collect_desc(mvm, desc, trigger); | ||
692 | } | ||
693 | |||
694 | int iwl_mvm_fw_dbg_collect_trig(struct iwl_mvm *mvm, | ||
695 | struct iwl_fw_dbg_trigger_tlv *trigger, | ||
696 | const char *fmt, ...) | ||
697 | { | ||
698 | u16 occurrences = le16_to_cpu(trigger->occurrences); | ||
699 | int ret, len = 0; | ||
700 | char buf[64]; | ||
701 | |||
702 | if (!occurrences) | ||
703 | return 0; | ||
704 | |||
705 | if (fmt) { | ||
706 | va_list ap; | ||
707 | |||
708 | buf[sizeof(buf) - 1] = '\0'; | ||
709 | |||
710 | va_start(ap, fmt); | ||
711 | vsnprintf(buf, sizeof(buf), fmt, ap); | ||
712 | va_end(ap); | ||
713 | |||
714 | /* check for truncation */ | ||
715 | if (WARN_ON_ONCE(buf[sizeof(buf) - 1])) | ||
716 | buf[sizeof(buf) - 1] = '\0'; | ||
717 | |||
718 | len = strlen(buf) + 1; | ||
719 | } | ||
720 | |||
721 | ret = iwl_mvm_fw_dbg_collect(mvm, le32_to_cpu(trigger->id), buf, len, | ||
722 | trigger); | ||
723 | |||
724 | if (ret) | ||
725 | return ret; | ||
726 | |||
727 | trigger->occurrences = cpu_to_le16(occurrences - 1); | ||
728 | return 0; | ||
729 | } | ||
730 | |||
731 | static inline void iwl_mvm_restart_early_start(struct iwl_mvm *mvm) | ||
732 | { | ||
733 | if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) | ||
734 | iwl_clear_bits_prph(mvm->trans, MON_BUFF_SAMPLE_CTL, 0x100); | ||
735 | else | ||
736 | iwl_write_prph(mvm->trans, DBGC_IN_SAMPLE, 1); | ||
737 | } | ||
738 | |||
739 | int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, u8 conf_id) | ||
740 | { | ||
741 | u8 *ptr; | ||
742 | int ret; | ||
743 | int i; | ||
744 | |||
745 | if (WARN_ONCE(conf_id >= ARRAY_SIZE(mvm->fw->dbg_conf_tlv), | ||
746 | "Invalid configuration %d\n", conf_id)) | ||
747 | return -EINVAL; | ||
748 | |||
749 | /* EARLY START - firmware's configuration is hard coded */ | ||
750 | if ((!mvm->fw->dbg_conf_tlv[conf_id] || | ||
751 | !mvm->fw->dbg_conf_tlv[conf_id]->num_of_hcmds) && | ||
752 | conf_id == FW_DBG_START_FROM_ALIVE) { | ||
753 | iwl_mvm_restart_early_start(mvm); | ||
754 | return 0; | ||
755 | } | ||
756 | |||
757 | if (!mvm->fw->dbg_conf_tlv[conf_id]) | ||
758 | return -EINVAL; | ||
759 | |||
760 | if (mvm->fw_dbg_conf != FW_DBG_INVALID) | ||
761 | IWL_WARN(mvm, "FW already configured (%d) - re-configuring\n", | ||
762 | mvm->fw_dbg_conf); | ||
763 | |||
764 | /* Send all HCMDs for configuring the FW debug */ | ||
765 | ptr = (void *)&mvm->fw->dbg_conf_tlv[conf_id]->hcmd; | ||
766 | for (i = 0; i < mvm->fw->dbg_conf_tlv[conf_id]->num_of_hcmds; i++) { | ||
767 | struct iwl_fw_dbg_conf_hcmd *cmd = (void *)ptr; | ||
768 | |||
769 | ret = iwl_mvm_send_cmd_pdu(mvm, cmd->id, 0, | ||
770 | le16_to_cpu(cmd->len), cmd->data); | ||
771 | if (ret) | ||
772 | return ret; | ||
773 | |||
774 | ptr += sizeof(*cmd); | ||
775 | ptr += le16_to_cpu(cmd->len); | ||
776 | } | ||
777 | |||
778 | mvm->fw_dbg_conf = conf_id; | ||
779 | return ret; | ||
780 | } | ||
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.h new file mode 100644 index 000000000000..32232d69ea44 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.h | |||
@@ -0,0 +1,150 @@ | |||
1 | /****************************************************************************** | ||
2 | * | ||
3 | * This file is provided under a dual BSD/GPLv2 license. When using or | ||
4 | * redistributing this file, you may do so under either license. | ||
5 | * | ||
6 | * GPL LICENSE SUMMARY | ||
7 | * | ||
8 | * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. | ||
9 | * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH | ||
10 | * Copyright(c) 2015 Intel Deutschland GmbH | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of version 2 of the GNU General Public License as | ||
14 | * published by the Free Software Foundation. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, but | ||
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
19 | * General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; | ||
23 | * | ||
24 | * The full GNU General Public License is included in this distribution | ||
25 | * in the file called COPYING. | ||
26 | * | ||
27 | * Contact Information: | ||
28 | * Intel Linux Wireless <ilw@linux.intel.com> | ||
29 | * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 | ||
30 | * | ||
31 | * BSD LICENSE | ||
32 | * | ||
33 | * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. | ||
34 | * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH | ||
35 | * Copyright(c) 2015 Intel Deutschland GmbH | ||
36 | * All rights reserved. | ||
37 | * | ||
38 | * Redistribution and use in source and binary forms, with or without | ||
39 | * modification, are permitted provided that the following conditions | ||
40 | * are met: | ||
41 | * | ||
42 | * * Redistributions of source code must retain the above copyright | ||
43 | * notice, this list of conditions and the following disclaimer. | ||
44 | * * Redistributions in binary form must reproduce the above copyright | ||
45 | * notice, this list of conditions and the following disclaimer in | ||
46 | * the documentation and/or other materials provided with the | ||
47 | * distribution. | ||
48 | * * Neither the name Intel Corporation nor the names of its | ||
49 | * contributors may be used to endorse or promote products derived | ||
50 | * from this software without specific prior written permission. | ||
51 | * | ||
52 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
53 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
54 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
55 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
56 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
57 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
58 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
59 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
60 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
61 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
62 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
63 | * | ||
64 | *****************************************************************************/ | ||
65 | |||
66 | #ifndef __mvm_fw_dbg_h__ | ||
67 | #define __mvm_fw_dbg_h__ | ||
68 | #include "iwl-fw-file.h" | ||
69 | #include "iwl-fw-error-dump.h" | ||
70 | #include "mvm.h" | ||
71 | |||
72 | void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm); | ||
73 | void iwl_mvm_free_fw_dump_desc(struct iwl_mvm *mvm); | ||
74 | int iwl_mvm_fw_dbg_collect_desc(struct iwl_mvm *mvm, | ||
75 | struct iwl_mvm_dump_desc *desc, | ||
76 | struct iwl_fw_dbg_trigger_tlv *trigger); | ||
77 | int iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm, enum iwl_fw_dbg_trigger trig, | ||
78 | const char *str, size_t len, | ||
79 | struct iwl_fw_dbg_trigger_tlv *trigger); | ||
80 | int iwl_mvm_fw_dbg_collect_trig(struct iwl_mvm *mvm, | ||
81 | struct iwl_fw_dbg_trigger_tlv *trigger, | ||
82 | const char *fmt, ...) __printf(3, 4); | ||
83 | int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, u8 id); | ||
84 | |||
85 | #define iwl_fw_dbg_trigger_enabled(fw, id) ({ \ | ||
86 | void *__dbg_trigger = (fw)->dbg_trigger_tlv[(id)]; \ | ||
87 | unlikely(__dbg_trigger); \ | ||
88 | }) | ||
89 | |||
90 | static inline struct iwl_fw_dbg_trigger_tlv* | ||
91 | _iwl_fw_dbg_get_trigger(const struct iwl_fw *fw, enum iwl_fw_dbg_trigger id) | ||
92 | { | ||
93 | return fw->dbg_trigger_tlv[id]; | ||
94 | } | ||
95 | |||
96 | #define iwl_fw_dbg_get_trigger(fw, id) ({ \ | ||
97 | BUILD_BUG_ON(!__builtin_constant_p(id)); \ | ||
98 | BUILD_BUG_ON((id) >= FW_DBG_TRIGGER_MAX); \ | ||
99 | _iwl_fw_dbg_get_trigger((fw), (id)); \ | ||
100 | }) | ||
101 | |||
102 | static inline bool | ||
103 | iwl_fw_dbg_trigger_vif_match(struct iwl_fw_dbg_trigger_tlv *trig, | ||
104 | struct ieee80211_vif *vif) | ||
105 | { | ||
106 | u32 trig_vif = le32_to_cpu(trig->vif_type); | ||
107 | |||
108 | return trig_vif == IWL_FW_DBG_CONF_VIF_ANY || vif->type == trig_vif; | ||
109 | } | ||
110 | |||
111 | static inline bool | ||
112 | iwl_fw_dbg_trigger_stop_conf_match(struct iwl_mvm *mvm, | ||
113 | struct iwl_fw_dbg_trigger_tlv *trig) | ||
114 | { | ||
115 | return ((trig->mode & IWL_FW_DBG_TRIGGER_STOP) && | ||
116 | (mvm->fw_dbg_conf == FW_DBG_INVALID || | ||
117 | (BIT(mvm->fw_dbg_conf) & le32_to_cpu(trig->stop_conf_ids)))); | ||
118 | } | ||
119 | |||
120 | static inline bool | ||
121 | iwl_fw_dbg_trigger_check_stop(struct iwl_mvm *mvm, | ||
122 | struct ieee80211_vif *vif, | ||
123 | struct iwl_fw_dbg_trigger_tlv *trig) | ||
124 | { | ||
125 | if (vif && !iwl_fw_dbg_trigger_vif_match(trig, vif)) | ||
126 | return false; | ||
127 | |||
128 | return iwl_fw_dbg_trigger_stop_conf_match(mvm, trig); | ||
129 | } | ||
130 | |||
131 | static inline void | ||
132 | _iwl_fw_dbg_trigger_simple_stop(struct iwl_mvm *mvm, | ||
133 | struct ieee80211_vif *vif, | ||
134 | struct iwl_fw_dbg_trigger_tlv *trigger) | ||
135 | { | ||
136 | if (!trigger) | ||
137 | return; | ||
138 | |||
139 | if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trigger)) | ||
140 | return; | ||
141 | |||
142 | iwl_mvm_fw_dbg_collect_trig(mvm, trigger, NULL); | ||
143 | } | ||
144 | |||
145 | #define iwl_fw_dbg_trigger_simple_stop(mvm, vif, trig) \ | ||
146 | _iwl_fw_dbg_trigger_simple_stop((mvm), (vif), \ | ||
147 | iwl_fw_dbg_get_trigger((mvm)->fw,\ | ||
148 | (trig))) | ||
149 | |||
150 | #endif /* __mvm_fw_dbg_h__ */ | ||
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index d906fa13ba97..05c933313f97 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c | |||
@@ -74,6 +74,7 @@ | |||
74 | #include "iwl-eeprom-parse.h" | 74 | #include "iwl-eeprom-parse.h" |
75 | 75 | ||
76 | #include "mvm.h" | 76 | #include "mvm.h" |
77 | #include "fw-dbg.h" | ||
77 | #include "iwl-phy-db.h" | 78 | #include "iwl-phy-db.h" |
78 | 79 | ||
79 | #define MVM_UCODE_ALIVE_TIMEOUT HZ | 80 | #define MVM_UCODE_ALIVE_TIMEOUT HZ |
@@ -805,137 +806,6 @@ static void iwl_mvm_get_shared_mem_conf(struct iwl_mvm *mvm) | |||
805 | iwl_free_resp(&cmd); | 806 | iwl_free_resp(&cmd); |
806 | } | 807 | } |
807 | 808 | ||
808 | int iwl_mvm_fw_dbg_collect_desc(struct iwl_mvm *mvm, | ||
809 | struct iwl_mvm_dump_desc *desc, | ||
810 | struct iwl_fw_dbg_trigger_tlv *trigger) | ||
811 | { | ||
812 | unsigned int delay = 0; | ||
813 | |||
814 | if (trigger) | ||
815 | delay = msecs_to_jiffies(le32_to_cpu(trigger->stop_delay)); | ||
816 | |||
817 | if (test_and_set_bit(IWL_MVM_STATUS_DUMPING_FW_LOG, &mvm->status)) | ||
818 | return -EBUSY; | ||
819 | |||
820 | if (WARN_ON(mvm->fw_dump_desc)) | ||
821 | iwl_mvm_free_fw_dump_desc(mvm); | ||
822 | |||
823 | IWL_WARN(mvm, "Collecting data: trigger %d fired.\n", | ||
824 | le32_to_cpu(desc->trig_desc.type)); | ||
825 | |||
826 | mvm->fw_dump_desc = desc; | ||
827 | mvm->fw_dump_trig = trigger; | ||
828 | |||
829 | queue_delayed_work(system_wq, &mvm->fw_dump_wk, delay); | ||
830 | |||
831 | return 0; | ||
832 | } | ||
833 | |||
834 | int iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm, enum iwl_fw_dbg_trigger trig, | ||
835 | const char *str, size_t len, | ||
836 | struct iwl_fw_dbg_trigger_tlv *trigger) | ||
837 | { | ||
838 | struct iwl_mvm_dump_desc *desc; | ||
839 | |||
840 | desc = kzalloc(sizeof(*desc) + len, GFP_ATOMIC); | ||
841 | if (!desc) | ||
842 | return -ENOMEM; | ||
843 | |||
844 | desc->len = len; | ||
845 | desc->trig_desc.type = cpu_to_le32(trig); | ||
846 | memcpy(desc->trig_desc.data, str, len); | ||
847 | |||
848 | return iwl_mvm_fw_dbg_collect_desc(mvm, desc, trigger); | ||
849 | } | ||
850 | |||
851 | int iwl_mvm_fw_dbg_collect_trig(struct iwl_mvm *mvm, | ||
852 | struct iwl_fw_dbg_trigger_tlv *trigger, | ||
853 | const char *fmt, ...) | ||
854 | { | ||
855 | u16 occurrences = le16_to_cpu(trigger->occurrences); | ||
856 | int ret, len = 0; | ||
857 | char buf[64]; | ||
858 | |||
859 | if (!occurrences) | ||
860 | return 0; | ||
861 | |||
862 | if (fmt) { | ||
863 | va_list ap; | ||
864 | |||
865 | buf[sizeof(buf) - 1] = '\0'; | ||
866 | |||
867 | va_start(ap, fmt); | ||
868 | vsnprintf(buf, sizeof(buf), fmt, ap); | ||
869 | va_end(ap); | ||
870 | |||
871 | /* check for truncation */ | ||
872 | if (WARN_ON_ONCE(buf[sizeof(buf) - 1])) | ||
873 | buf[sizeof(buf) - 1] = '\0'; | ||
874 | |||
875 | len = strlen(buf) + 1; | ||
876 | } | ||
877 | |||
878 | ret = iwl_mvm_fw_dbg_collect(mvm, le32_to_cpu(trigger->id), buf, len, | ||
879 | trigger); | ||
880 | |||
881 | if (ret) | ||
882 | return ret; | ||
883 | |||
884 | trigger->occurrences = cpu_to_le16(occurrences - 1); | ||
885 | return 0; | ||
886 | } | ||
887 | |||
888 | static inline void iwl_mvm_restart_early_start(struct iwl_mvm *mvm) | ||
889 | { | ||
890 | if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) | ||
891 | iwl_clear_bits_prph(mvm->trans, MON_BUFF_SAMPLE_CTL, 0x100); | ||
892 | else | ||
893 | iwl_write_prph(mvm->trans, DBGC_IN_SAMPLE, 1); | ||
894 | } | ||
895 | |||
896 | int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, u8 conf_id) | ||
897 | { | ||
898 | u8 *ptr; | ||
899 | int ret; | ||
900 | int i; | ||
901 | |||
902 | if (WARN_ONCE(conf_id >= ARRAY_SIZE(mvm->fw->dbg_conf_tlv), | ||
903 | "Invalid configuration %d\n", conf_id)) | ||
904 | return -EINVAL; | ||
905 | |||
906 | /* EARLY START - firmware's configuration is hard coded */ | ||
907 | if ((!mvm->fw->dbg_conf_tlv[conf_id] || | ||
908 | !mvm->fw->dbg_conf_tlv[conf_id]->num_of_hcmds) && | ||
909 | conf_id == FW_DBG_START_FROM_ALIVE) { | ||
910 | iwl_mvm_restart_early_start(mvm); | ||
911 | return 0; | ||
912 | } | ||
913 | |||
914 | if (!mvm->fw->dbg_conf_tlv[conf_id]) | ||
915 | return -EINVAL; | ||
916 | |||
917 | if (mvm->fw_dbg_conf != FW_DBG_INVALID) | ||
918 | IWL_WARN(mvm, "FW already configured (%d) - re-configuring\n", | ||
919 | mvm->fw_dbg_conf); | ||
920 | |||
921 | /* Send all HCMDs for configuring the FW debug */ | ||
922 | ptr = (void *)&mvm->fw->dbg_conf_tlv[conf_id]->hcmd; | ||
923 | for (i = 0; i < mvm->fw->dbg_conf_tlv[conf_id]->num_of_hcmds; i++) { | ||
924 | struct iwl_fw_dbg_conf_hcmd *cmd = (void *)ptr; | ||
925 | |||
926 | ret = iwl_mvm_send_cmd_pdu(mvm, cmd->id, 0, | ||
927 | le16_to_cpu(cmd->len), cmd->data); | ||
928 | if (ret) | ||
929 | return ret; | ||
930 | |||
931 | ptr += sizeof(*cmd); | ||
932 | ptr += le16_to_cpu(cmd->len); | ||
933 | } | ||
934 | |||
935 | mvm->fw_dbg_conf = conf_id; | ||
936 | return ret; | ||
937 | } | ||
938 | |||
939 | static int iwl_mvm_config_ltr(struct iwl_mvm *mvm) | 809 | static int iwl_mvm_config_ltr(struct iwl_mvm *mvm) |
940 | { | 810 | { |
941 | struct iwl_ltr_config_cmd cmd = { | 811 | struct iwl_ltr_config_cmd cmd = { |
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index ad7ad720d2e7..ee511aae727d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c | |||
@@ -72,6 +72,7 @@ | |||
72 | #include "fw-api.h" | 72 | #include "fw-api.h" |
73 | #include "mvm.h" | 73 | #include "mvm.h" |
74 | #include "time-event.h" | 74 | #include "time-event.h" |
75 | #include "fw-dbg.h" | ||
75 | 76 | ||
76 | const u8 iwl_mvm_ac_to_tx_fifo[] = { | 77 | const u8 iwl_mvm_ac_to_tx_fifo[] = { |
77 | IWL_MVM_TX_FIFO_VO, | 78 | IWL_MVM_TX_FIFO_VO, |
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index b83334f27b44..327703d5d4ba 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | |||
@@ -70,6 +70,7 @@ | |||
70 | #include <linux/ip.h> | 70 | #include <linux/ip.h> |
71 | #include <linux/if_arp.h> | 71 | #include <linux/if_arp.h> |
72 | #include <linux/devcoredump.h> | 72 | #include <linux/devcoredump.h> |
73 | #include <linux/time.h> | ||
73 | #include <net/mac80211.h> | 74 | #include <net/mac80211.h> |
74 | #include <net/ieee80211_radiotap.h> | 75 | #include <net/ieee80211_radiotap.h> |
75 | #include <net/tcp.h> | 76 | #include <net/tcp.h> |
@@ -86,6 +87,7 @@ | |||
86 | #include "iwl-prph.h" | 87 | #include "iwl-prph.h" |
87 | #include "iwl-csr.h" | 88 | #include "iwl-csr.h" |
88 | #include "iwl-nvm-parse.h" | 89 | #include "iwl-nvm-parse.h" |
90 | #include "fw-dbg.h" | ||
89 | 91 | ||
90 | static const struct ieee80211_iface_limit iwl_mvm_limits[] = { | 92 | static const struct ieee80211_iface_limit iwl_mvm_limits[] = { |
91 | { | 93 | { |
@@ -939,584 +941,6 @@ static void iwl_mvm_cleanup_iterator(void *data, u8 *mac, | |||
939 | memset(&mvmvif->bf_data, 0, sizeof(mvmvif->bf_data)); | 941 | memset(&mvmvif->bf_data, 0, sizeof(mvmvif->bf_data)); |
940 | } | 942 | } |
941 | 943 | ||
942 | static ssize_t iwl_mvm_read_coredump(char *buffer, loff_t offset, size_t count, | ||
943 | const void *data, size_t datalen) | ||
944 | { | ||
945 | const struct iwl_mvm_dump_ptrs *dump_ptrs = data; | ||
946 | ssize_t bytes_read; | ||
947 | ssize_t bytes_read_trans; | ||
948 | |||
949 | if (offset < dump_ptrs->op_mode_len) { | ||
950 | bytes_read = min_t(ssize_t, count, | ||
951 | dump_ptrs->op_mode_len - offset); | ||
952 | memcpy(buffer, (u8 *)dump_ptrs->op_mode_ptr + offset, | ||
953 | bytes_read); | ||
954 | offset += bytes_read; | ||
955 | count -= bytes_read; | ||
956 | |||
957 | if (count == 0) | ||
958 | return bytes_read; | ||
959 | } else { | ||
960 | bytes_read = 0; | ||
961 | } | ||
962 | |||
963 | if (!dump_ptrs->trans_ptr) | ||
964 | return bytes_read; | ||
965 | |||
966 | offset -= dump_ptrs->op_mode_len; | ||
967 | bytes_read_trans = min_t(ssize_t, count, | ||
968 | dump_ptrs->trans_ptr->len - offset); | ||
969 | memcpy(buffer + bytes_read, | ||
970 | (u8 *)dump_ptrs->trans_ptr->data + offset, | ||
971 | bytes_read_trans); | ||
972 | |||
973 | return bytes_read + bytes_read_trans; | ||
974 | } | ||
975 | |||
976 | static void iwl_mvm_free_coredump(const void *data) | ||
977 | { | ||
978 | const struct iwl_mvm_dump_ptrs *fw_error_dump = data; | ||
979 | |||
980 | vfree(fw_error_dump->op_mode_ptr); | ||
981 | vfree(fw_error_dump->trans_ptr); | ||
982 | kfree(fw_error_dump); | ||
983 | } | ||
984 | |||
985 | static void iwl_mvm_dump_fifos(struct iwl_mvm *mvm, | ||
986 | struct iwl_fw_error_dump_data **dump_data) | ||
987 | { | ||
988 | struct iwl_fw_error_dump_fifo *fifo_hdr; | ||
989 | u32 *fifo_data; | ||
990 | u32 fifo_len; | ||
991 | unsigned long flags; | ||
992 | int i, j; | ||
993 | |||
994 | if (!iwl_trans_grab_nic_access(mvm->trans, false, &flags)) | ||
995 | return; | ||
996 | |||
997 | /* Pull RXF data from all RXFs */ | ||
998 | for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.rxfifo_size); i++) { | ||
999 | /* | ||
1000 | * Keep aside the additional offset that might be needed for | ||
1001 | * next RXF | ||
1002 | */ | ||
1003 | u32 offset_diff = RXF_DIFF_FROM_PREV * i; | ||
1004 | |||
1005 | fifo_hdr = (void *)(*dump_data)->data; | ||
1006 | fifo_data = (void *)fifo_hdr->data; | ||
1007 | fifo_len = mvm->shared_mem_cfg.rxfifo_size[i]; | ||
1008 | |||
1009 | /* No need to try to read the data if the length is 0 */ | ||
1010 | if (fifo_len == 0) | ||
1011 | continue; | ||
1012 | |||
1013 | /* Add a TLV for the RXF */ | ||
1014 | (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RXF); | ||
1015 | (*dump_data)->len = cpu_to_le32(fifo_len + sizeof(*fifo_hdr)); | ||
1016 | |||
1017 | fifo_hdr->fifo_num = cpu_to_le32(i); | ||
1018 | fifo_hdr->available_bytes = | ||
1019 | cpu_to_le32(iwl_trans_read_prph(mvm->trans, | ||
1020 | RXF_RD_D_SPACE + | ||
1021 | offset_diff)); | ||
1022 | fifo_hdr->wr_ptr = | ||
1023 | cpu_to_le32(iwl_trans_read_prph(mvm->trans, | ||
1024 | RXF_RD_WR_PTR + | ||
1025 | offset_diff)); | ||
1026 | fifo_hdr->rd_ptr = | ||
1027 | cpu_to_le32(iwl_trans_read_prph(mvm->trans, | ||
1028 | RXF_RD_RD_PTR + | ||
1029 | offset_diff)); | ||
1030 | fifo_hdr->fence_ptr = | ||
1031 | cpu_to_le32(iwl_trans_read_prph(mvm->trans, | ||
1032 | RXF_RD_FENCE_PTR + | ||
1033 | offset_diff)); | ||
1034 | fifo_hdr->fence_mode = | ||
1035 | cpu_to_le32(iwl_trans_read_prph(mvm->trans, | ||
1036 | RXF_SET_FENCE_MODE + | ||
1037 | offset_diff)); | ||
1038 | |||
1039 | /* Lock fence */ | ||
1040 | iwl_trans_write_prph(mvm->trans, | ||
1041 | RXF_SET_FENCE_MODE + offset_diff, 0x1); | ||
1042 | /* Set fence pointer to the same place like WR pointer */ | ||
1043 | iwl_trans_write_prph(mvm->trans, | ||
1044 | RXF_LD_WR2FENCE + offset_diff, 0x1); | ||
1045 | /* Set fence offset */ | ||
1046 | iwl_trans_write_prph(mvm->trans, | ||
1047 | RXF_LD_FENCE_OFFSET_ADDR + offset_diff, | ||
1048 | 0x0); | ||
1049 | |||
1050 | /* Read FIFO */ | ||
1051 | fifo_len /= sizeof(u32); /* Size in DWORDS */ | ||
1052 | for (j = 0; j < fifo_len; j++) | ||
1053 | fifo_data[j] = iwl_trans_read_prph(mvm->trans, | ||
1054 | RXF_FIFO_RD_FENCE_INC + | ||
1055 | offset_diff); | ||
1056 | *dump_data = iwl_fw_error_next_data(*dump_data); | ||
1057 | } | ||
1058 | |||
1059 | /* Pull TXF data from all TXFs */ | ||
1060 | for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.txfifo_size); i++) { | ||
1061 | /* Mark the number of TXF we're pulling now */ | ||
1062 | iwl_trans_write_prph(mvm->trans, TXF_LARC_NUM, i); | ||
1063 | |||
1064 | fifo_hdr = (void *)(*dump_data)->data; | ||
1065 | fifo_data = (void *)fifo_hdr->data; | ||
1066 | fifo_len = mvm->shared_mem_cfg.txfifo_size[i]; | ||
1067 | |||
1068 | /* No need to try to read the data if the length is 0 */ | ||
1069 | if (fifo_len == 0) | ||
1070 | continue; | ||
1071 | |||
1072 | /* Add a TLV for the FIFO */ | ||
1073 | (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_TXF); | ||
1074 | (*dump_data)->len = cpu_to_le32(fifo_len + sizeof(*fifo_hdr)); | ||
1075 | |||
1076 | fifo_hdr->fifo_num = cpu_to_le32(i); | ||
1077 | fifo_hdr->available_bytes = | ||
1078 | cpu_to_le32(iwl_trans_read_prph(mvm->trans, | ||
1079 | TXF_FIFO_ITEM_CNT)); | ||
1080 | fifo_hdr->wr_ptr = | ||
1081 | cpu_to_le32(iwl_trans_read_prph(mvm->trans, | ||
1082 | TXF_WR_PTR)); | ||
1083 | fifo_hdr->rd_ptr = | ||
1084 | cpu_to_le32(iwl_trans_read_prph(mvm->trans, | ||
1085 | TXF_RD_PTR)); | ||
1086 | fifo_hdr->fence_ptr = | ||
1087 | cpu_to_le32(iwl_trans_read_prph(mvm->trans, | ||
1088 | TXF_FENCE_PTR)); | ||
1089 | fifo_hdr->fence_mode = | ||
1090 | cpu_to_le32(iwl_trans_read_prph(mvm->trans, | ||
1091 | TXF_LOCK_FENCE)); | ||
1092 | |||
1093 | /* Set the TXF_READ_MODIFY_ADDR to TXF_WR_PTR */ | ||
1094 | iwl_trans_write_prph(mvm->trans, TXF_READ_MODIFY_ADDR, | ||
1095 | TXF_WR_PTR); | ||
1096 | |||
1097 | /* Dummy-read to advance the read pointer to the head */ | ||
1098 | iwl_trans_read_prph(mvm->trans, TXF_READ_MODIFY_DATA); | ||
1099 | |||
1100 | /* Read FIFO */ | ||
1101 | fifo_len /= sizeof(u32); /* Size in DWORDS */ | ||
1102 | for (j = 0; j < fifo_len; j++) | ||
1103 | fifo_data[j] = iwl_trans_read_prph(mvm->trans, | ||
1104 | TXF_READ_MODIFY_DATA); | ||
1105 | *dump_data = iwl_fw_error_next_data(*dump_data); | ||
1106 | } | ||
1107 | |||
1108 | iwl_trans_release_nic_access(mvm->trans, &flags); | ||
1109 | } | ||
1110 | |||
1111 | void iwl_mvm_free_fw_dump_desc(struct iwl_mvm *mvm) | ||
1112 | { | ||
1113 | if (mvm->fw_dump_desc == &iwl_mvm_dump_desc_assert || | ||
1114 | !mvm->fw_dump_desc) | ||
1115 | return; | ||
1116 | |||
1117 | kfree(mvm->fw_dump_desc); | ||
1118 | mvm->fw_dump_desc = NULL; | ||
1119 | } | ||
1120 | |||
1121 | #define IWL8260_ICCM_OFFSET 0x44000 /* Only for B-step */ | ||
1122 | #define IWL8260_ICCM_LEN 0xC000 /* Only for B-step */ | ||
1123 | |||
1124 | static const struct { | ||
1125 | u32 start, end; | ||
1126 | } iwl_prph_dump_addr[] = { | ||
1127 | { .start = 0x00a00000, .end = 0x00a00000 }, | ||
1128 | { .start = 0x00a0000c, .end = 0x00a00024 }, | ||
1129 | { .start = 0x00a0002c, .end = 0x00a0003c }, | ||
1130 | { .start = 0x00a00410, .end = 0x00a00418 }, | ||
1131 | { .start = 0x00a00420, .end = 0x00a00420 }, | ||
1132 | { .start = 0x00a00428, .end = 0x00a00428 }, | ||
1133 | { .start = 0x00a00430, .end = 0x00a0043c }, | ||
1134 | { .start = 0x00a00444, .end = 0x00a00444 }, | ||
1135 | { .start = 0x00a004c0, .end = 0x00a004cc }, | ||
1136 | { .start = 0x00a004d8, .end = 0x00a004d8 }, | ||
1137 | { .start = 0x00a004e0, .end = 0x00a004f0 }, | ||
1138 | { .start = 0x00a00840, .end = 0x00a00840 }, | ||
1139 | { .start = 0x00a00850, .end = 0x00a00858 }, | ||
1140 | { .start = 0x00a01004, .end = 0x00a01008 }, | ||
1141 | { .start = 0x00a01010, .end = 0x00a01010 }, | ||
1142 | { .start = 0x00a01018, .end = 0x00a01018 }, | ||
1143 | { .start = 0x00a01024, .end = 0x00a01024 }, | ||
1144 | { .start = 0x00a0102c, .end = 0x00a01034 }, | ||
1145 | { .start = 0x00a0103c, .end = 0x00a01040 }, | ||
1146 | { .start = 0x00a01048, .end = 0x00a01094 }, | ||
1147 | { .start = 0x00a01c00, .end = 0x00a01c20 }, | ||
1148 | { .start = 0x00a01c58, .end = 0x00a01c58 }, | ||
1149 | { .start = 0x00a01c7c, .end = 0x00a01c7c }, | ||
1150 | { .start = 0x00a01c28, .end = 0x00a01c54 }, | ||
1151 | { .start = 0x00a01c5c, .end = 0x00a01c5c }, | ||
1152 | { .start = 0x00a01c60, .end = 0x00a01cdc }, | ||
1153 | { .start = 0x00a01ce0, .end = 0x00a01d0c }, | ||
1154 | { .start = 0x00a01d18, .end = 0x00a01d20 }, | ||
1155 | { .start = 0x00a01d2c, .end = 0x00a01d30 }, | ||
1156 | { .start = 0x00a01d40, .end = 0x00a01d5c }, | ||
1157 | { .start = 0x00a01d80, .end = 0x00a01d80 }, | ||
1158 | { .start = 0x00a01d98, .end = 0x00a01d9c }, | ||
1159 | { .start = 0x00a01da8, .end = 0x00a01da8 }, | ||
1160 | { .start = 0x00a01db8, .end = 0x00a01df4 }, | ||
1161 | { .start = 0x00a01dc0, .end = 0x00a01dfc }, | ||
1162 | { .start = 0x00a01e00, .end = 0x00a01e2c }, | ||
1163 | { .start = 0x00a01e40, .end = 0x00a01e60 }, | ||
1164 | { .start = 0x00a01e68, .end = 0x00a01e6c }, | ||
1165 | { .start = 0x00a01e74, .end = 0x00a01e74 }, | ||
1166 | { .start = 0x00a01e84, .end = 0x00a01e90 }, | ||
1167 | { .start = 0x00a01e9c, .end = 0x00a01ec4 }, | ||
1168 | { .start = 0x00a01ed0, .end = 0x00a01ee0 }, | ||
1169 | { .start = 0x00a01f00, .end = 0x00a01f1c }, | ||
1170 | { .start = 0x00a01f44, .end = 0x00a01ffc }, | ||
1171 | { .start = 0x00a02000, .end = 0x00a02048 }, | ||
1172 | { .start = 0x00a02068, .end = 0x00a020f0 }, | ||
1173 | { .start = 0x00a02100, .end = 0x00a02118 }, | ||
1174 | { .start = 0x00a02140, .end = 0x00a0214c }, | ||
1175 | { .start = 0x00a02168, .end = 0x00a0218c }, | ||
1176 | { .start = 0x00a021c0, .end = 0x00a021c0 }, | ||
1177 | { .start = 0x00a02400, .end = 0x00a02410 }, | ||
1178 | { .start = 0x00a02418, .end = 0x00a02420 }, | ||
1179 | { .start = 0x00a02428, .end = 0x00a0242c }, | ||
1180 | { .start = 0x00a02434, .end = 0x00a02434 }, | ||
1181 | { .start = 0x00a02440, .end = 0x00a02460 }, | ||
1182 | { .start = 0x00a02468, .end = 0x00a024b0 }, | ||
1183 | { .start = 0x00a024c8, .end = 0x00a024cc }, | ||
1184 | { .start = 0x00a02500, .end = 0x00a02504 }, | ||
1185 | { .start = 0x00a0250c, .end = 0x00a02510 }, | ||
1186 | { .start = 0x00a02540, .end = 0x00a02554 }, | ||
1187 | { .start = 0x00a02580, .end = 0x00a025f4 }, | ||
1188 | { .start = 0x00a02600, .end = 0x00a0260c }, | ||
1189 | { .start = 0x00a02648, .end = 0x00a02650 }, | ||
1190 | { .start = 0x00a02680, .end = 0x00a02680 }, | ||
1191 | { .start = 0x00a026c0, .end = 0x00a026d0 }, | ||
1192 | { .start = 0x00a02700, .end = 0x00a0270c }, | ||
1193 | { .start = 0x00a02804, .end = 0x00a02804 }, | ||
1194 | { .start = 0x00a02818, .end = 0x00a0281c }, | ||
1195 | { .start = 0x00a02c00, .end = 0x00a02db4 }, | ||
1196 | { .start = 0x00a02df4, .end = 0x00a02fb0 }, | ||
1197 | { .start = 0x00a03000, .end = 0x00a03014 }, | ||
1198 | { .start = 0x00a0301c, .end = 0x00a0302c }, | ||
1199 | { .start = 0x00a03034, .end = 0x00a03038 }, | ||
1200 | { .start = 0x00a03040, .end = 0x00a03048 }, | ||
1201 | { .start = 0x00a03060, .end = 0x00a03068 }, | ||
1202 | { .start = 0x00a03070, .end = 0x00a03074 }, | ||
1203 | { .start = 0x00a0307c, .end = 0x00a0307c }, | ||
1204 | { .start = 0x00a03080, .end = 0x00a03084 }, | ||
1205 | { .start = 0x00a0308c, .end = 0x00a03090 }, | ||
1206 | { .start = 0x00a03098, .end = 0x00a03098 }, | ||
1207 | { .start = 0x00a030a0, .end = 0x00a030a0 }, | ||
1208 | { .start = 0x00a030a8, .end = 0x00a030b4 }, | ||
1209 | { .start = 0x00a030bc, .end = 0x00a030bc }, | ||
1210 | { .start = 0x00a030c0, .end = 0x00a0312c }, | ||
1211 | { .start = 0x00a03c00, .end = 0x00a03c5c }, | ||
1212 | { .start = 0x00a04400, .end = 0x00a04454 }, | ||
1213 | { .start = 0x00a04460, .end = 0x00a04474 }, | ||
1214 | { .start = 0x00a044c0, .end = 0x00a044ec }, | ||
1215 | { .start = 0x00a04500, .end = 0x00a04504 }, | ||
1216 | { .start = 0x00a04510, .end = 0x00a04538 }, | ||
1217 | { .start = 0x00a04540, .end = 0x00a04548 }, | ||
1218 | { .start = 0x00a04560, .end = 0x00a0457c }, | ||
1219 | { .start = 0x00a04590, .end = 0x00a04598 }, | ||
1220 | { .start = 0x00a045c0, .end = 0x00a045f4 }, | ||
1221 | }; | ||
1222 | |||
1223 | static u32 iwl_dump_prph(struct iwl_trans *trans, | ||
1224 | struct iwl_fw_error_dump_data **data) | ||
1225 | { | ||
1226 | struct iwl_fw_error_dump_prph *prph; | ||
1227 | unsigned long flags; | ||
1228 | u32 prph_len = 0, i; | ||
1229 | |||
1230 | if (!iwl_trans_grab_nic_access(trans, false, &flags)) | ||
1231 | return 0; | ||
1232 | |||
1233 | for (i = 0; i < ARRAY_SIZE(iwl_prph_dump_addr); i++) { | ||
1234 | /* The range includes both boundaries */ | ||
1235 | int num_bytes_in_chunk = iwl_prph_dump_addr[i].end - | ||
1236 | iwl_prph_dump_addr[i].start + 4; | ||
1237 | int reg; | ||
1238 | __le32 *val; | ||
1239 | |||
1240 | prph_len += sizeof(**data) + sizeof(*prph) + num_bytes_in_chunk; | ||
1241 | |||
1242 | (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PRPH); | ||
1243 | (*data)->len = cpu_to_le32(sizeof(*prph) + | ||
1244 | num_bytes_in_chunk); | ||
1245 | prph = (void *)(*data)->data; | ||
1246 | prph->prph_start = cpu_to_le32(iwl_prph_dump_addr[i].start); | ||
1247 | val = (void *)prph->data; | ||
1248 | |||
1249 | for (reg = iwl_prph_dump_addr[i].start; | ||
1250 | reg <= iwl_prph_dump_addr[i].end; | ||
1251 | reg += 4) | ||
1252 | *val++ = cpu_to_le32(iwl_read_prph_no_grab(trans, | ||
1253 | reg)); | ||
1254 | |||
1255 | *data = iwl_fw_error_next_data(*data); | ||
1256 | } | ||
1257 | |||
1258 | iwl_trans_release_nic_access(trans, &flags); | ||
1259 | |||
1260 | return prph_len; | ||
1261 | } | ||
1262 | |||
1263 | void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) | ||
1264 | { | ||
1265 | struct iwl_fw_error_dump_file *dump_file; | ||
1266 | struct iwl_fw_error_dump_data *dump_data; | ||
1267 | struct iwl_fw_error_dump_info *dump_info; | ||
1268 | struct iwl_fw_error_dump_mem *dump_mem; | ||
1269 | struct iwl_fw_error_dump_trigger_desc *dump_trig; | ||
1270 | struct iwl_mvm_dump_ptrs *fw_error_dump; | ||
1271 | u32 sram_len, sram_ofs; | ||
1272 | u32 file_len, fifo_data_len = 0; | ||
1273 | u32 smem_len = mvm->cfg->smem_len; | ||
1274 | u32 sram2_len = mvm->cfg->dccm2_len; | ||
1275 | bool monitor_dump_only = false; | ||
1276 | int i; | ||
1277 | |||
1278 | lockdep_assert_held(&mvm->mutex); | ||
1279 | |||
1280 | /* there's no point in fw dump if the bus is dead */ | ||
1281 | if (test_bit(STATUS_TRANS_DEAD, &mvm->trans->status)) { | ||
1282 | IWL_ERR(mvm, "Skip fw error dump since bus is dead\n"); | ||
1283 | return; | ||
1284 | } | ||
1285 | |||
1286 | if (mvm->fw_dump_trig && | ||
1287 | mvm->fw_dump_trig->mode & IWL_FW_DBG_TRIGGER_MONITOR_ONLY) | ||
1288 | monitor_dump_only = true; | ||
1289 | |||
1290 | fw_error_dump = kzalloc(sizeof(*fw_error_dump), GFP_KERNEL); | ||
1291 | if (!fw_error_dump) | ||
1292 | return; | ||
1293 | |||
1294 | /* SRAM - include stack CCM if driver knows the values for it */ | ||
1295 | if (!mvm->cfg->dccm_offset || !mvm->cfg->dccm_len) { | ||
1296 | const struct fw_img *img; | ||
1297 | |||
1298 | img = &mvm->fw->img[mvm->cur_ucode]; | ||
1299 | sram_ofs = img->sec[IWL_UCODE_SECTION_DATA].offset; | ||
1300 | sram_len = img->sec[IWL_UCODE_SECTION_DATA].len; | ||
1301 | } else { | ||
1302 | sram_ofs = mvm->cfg->dccm_offset; | ||
1303 | sram_len = mvm->cfg->dccm_len; | ||
1304 | } | ||
1305 | |||
1306 | /* reading RXF/TXF sizes */ | ||
1307 | if (test_bit(STATUS_FW_ERROR, &mvm->trans->status)) { | ||
1308 | struct iwl_mvm_shared_mem_cfg *mem_cfg = &mvm->shared_mem_cfg; | ||
1309 | |||
1310 | fifo_data_len = 0; | ||
1311 | |||
1312 | /* Count RXF size */ | ||
1313 | for (i = 0; i < ARRAY_SIZE(mem_cfg->rxfifo_size); i++) { | ||
1314 | if (!mem_cfg->rxfifo_size[i]) | ||
1315 | continue; | ||
1316 | |||
1317 | /* Add header info */ | ||
1318 | fifo_data_len += mem_cfg->rxfifo_size[i] + | ||
1319 | sizeof(*dump_data) + | ||
1320 | sizeof(struct iwl_fw_error_dump_fifo); | ||
1321 | } | ||
1322 | |||
1323 | for (i = 0; i < ARRAY_SIZE(mem_cfg->txfifo_size); i++) { | ||
1324 | if (!mem_cfg->txfifo_size[i]) | ||
1325 | continue; | ||
1326 | |||
1327 | /* Add header info */ | ||
1328 | fifo_data_len += mem_cfg->txfifo_size[i] + | ||
1329 | sizeof(*dump_data) + | ||
1330 | sizeof(struct iwl_fw_error_dump_fifo); | ||
1331 | } | ||
1332 | } | ||
1333 | |||
1334 | file_len = sizeof(*dump_file) + | ||
1335 | sizeof(*dump_data) * 2 + | ||
1336 | sram_len + sizeof(*dump_mem) + | ||
1337 | fifo_data_len + | ||
1338 | sizeof(*dump_info); | ||
1339 | |||
1340 | /* Make room for the SMEM, if it exists */ | ||
1341 | if (smem_len) | ||
1342 | file_len += sizeof(*dump_data) + sizeof(*dump_mem) + smem_len; | ||
1343 | |||
1344 | /* Make room for the secondary SRAM, if it exists */ | ||
1345 | if (sram2_len) | ||
1346 | file_len += sizeof(*dump_data) + sizeof(*dump_mem) + sram2_len; | ||
1347 | |||
1348 | /* Make room for fw's virtual image pages, if it exists */ | ||
1349 | if (mvm->fw->img[mvm->cur_ucode].paging_mem_size) | ||
1350 | file_len += mvm->num_of_paging_blk * | ||
1351 | (sizeof(*dump_data) + | ||
1352 | sizeof(struct iwl_fw_error_dump_paging) + | ||
1353 | PAGING_BLOCK_SIZE); | ||
1354 | |||
1355 | /* If we only want a monitor dump, reset the file length */ | ||
1356 | if (monitor_dump_only) { | ||
1357 | file_len = sizeof(*dump_file) + sizeof(*dump_data) + | ||
1358 | sizeof(*dump_info); | ||
1359 | } | ||
1360 | |||
1361 | /* Make room for PRPH registers */ | ||
1362 | for (i = 0; i < ARRAY_SIZE(iwl_prph_dump_addr); i++) { | ||
1363 | /* The range includes both boundaries */ | ||
1364 | int num_bytes_in_chunk = iwl_prph_dump_addr[i].end - | ||
1365 | iwl_prph_dump_addr[i].start + 4; | ||
1366 | |||
1367 | file_len += sizeof(*dump_data) + | ||
1368 | sizeof(struct iwl_fw_error_dump_prph) + | ||
1369 | num_bytes_in_chunk; | ||
1370 | } | ||
1371 | |||
1372 | /* | ||
1373 | * In 8000 HW family B-step include the ICCM (which resides separately) | ||
1374 | */ | ||
1375 | if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000 && | ||
1376 | CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_B_STEP) | ||
1377 | file_len += sizeof(*dump_data) + sizeof(*dump_mem) + | ||
1378 | IWL8260_ICCM_LEN; | ||
1379 | |||
1380 | if (mvm->fw_dump_desc) | ||
1381 | file_len += sizeof(*dump_data) + sizeof(*dump_trig) + | ||
1382 | mvm->fw_dump_desc->len; | ||
1383 | |||
1384 | dump_file = vzalloc(file_len); | ||
1385 | if (!dump_file) { | ||
1386 | kfree(fw_error_dump); | ||
1387 | iwl_mvm_free_fw_dump_desc(mvm); | ||
1388 | return; | ||
1389 | } | ||
1390 | |||
1391 | fw_error_dump->op_mode_ptr = dump_file; | ||
1392 | |||
1393 | dump_file->barker = cpu_to_le32(IWL_FW_ERROR_DUMP_BARKER); | ||
1394 | dump_data = (void *)dump_file->data; | ||
1395 | |||
1396 | dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_DEV_FW_INFO); | ||
1397 | dump_data->len = cpu_to_le32(sizeof(*dump_info)); | ||
1398 | dump_info = (void *) dump_data->data; | ||
1399 | dump_info->device_family = | ||
1400 | mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000 ? | ||
1401 | cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_7) : | ||
1402 | cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_8); | ||
1403 | dump_info->hw_step = cpu_to_le32(CSR_HW_REV_STEP(mvm->trans->hw_rev)); | ||
1404 | memcpy(dump_info->fw_human_readable, mvm->fw->human_readable, | ||
1405 | sizeof(dump_info->fw_human_readable)); | ||
1406 | strncpy(dump_info->dev_human_readable, mvm->cfg->name, | ||
1407 | sizeof(dump_info->dev_human_readable)); | ||
1408 | strncpy(dump_info->bus_human_readable, mvm->dev->bus->name, | ||
1409 | sizeof(dump_info->bus_human_readable)); | ||
1410 | |||
1411 | dump_data = iwl_fw_error_next_data(dump_data); | ||
1412 | /* We only dump the FIFOs if the FW is in error state */ | ||
1413 | if (test_bit(STATUS_FW_ERROR, &mvm->trans->status)) | ||
1414 | iwl_mvm_dump_fifos(mvm, &dump_data); | ||
1415 | |||
1416 | if (mvm->fw_dump_desc) { | ||
1417 | dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_ERROR_INFO); | ||
1418 | dump_data->len = cpu_to_le32(sizeof(*dump_trig) + | ||
1419 | mvm->fw_dump_desc->len); | ||
1420 | dump_trig = (void *)dump_data->data; | ||
1421 | memcpy(dump_trig, &mvm->fw_dump_desc->trig_desc, | ||
1422 | sizeof(*dump_trig) + mvm->fw_dump_desc->len); | ||
1423 | |||
1424 | /* now we can free this copy */ | ||
1425 | iwl_mvm_free_fw_dump_desc(mvm); | ||
1426 | dump_data = iwl_fw_error_next_data(dump_data); | ||
1427 | } | ||
1428 | |||
1429 | /* In case we only want monitor dump, skip to dump trasport data */ | ||
1430 | if (monitor_dump_only) | ||
1431 | goto dump_trans_data; | ||
1432 | |||
1433 | dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); | ||
1434 | dump_data->len = cpu_to_le32(sram_len + sizeof(*dump_mem)); | ||
1435 | dump_mem = (void *)dump_data->data; | ||
1436 | dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM); | ||
1437 | dump_mem->offset = cpu_to_le32(sram_ofs); | ||
1438 | iwl_trans_read_mem_bytes(mvm->trans, sram_ofs, dump_mem->data, | ||
1439 | sram_len); | ||
1440 | |||
1441 | if (smem_len) { | ||
1442 | dump_data = iwl_fw_error_next_data(dump_data); | ||
1443 | dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); | ||
1444 | dump_data->len = cpu_to_le32(smem_len + sizeof(*dump_mem)); | ||
1445 | dump_mem = (void *)dump_data->data; | ||
1446 | dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SMEM); | ||
1447 | dump_mem->offset = cpu_to_le32(mvm->cfg->smem_offset); | ||
1448 | iwl_trans_read_mem_bytes(mvm->trans, mvm->cfg->smem_offset, | ||
1449 | dump_mem->data, smem_len); | ||
1450 | } | ||
1451 | |||
1452 | if (sram2_len) { | ||
1453 | dump_data = iwl_fw_error_next_data(dump_data); | ||
1454 | dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); | ||
1455 | dump_data->len = cpu_to_le32(sram2_len + sizeof(*dump_mem)); | ||
1456 | dump_mem = (void *)dump_data->data; | ||
1457 | dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM); | ||
1458 | dump_mem->offset = cpu_to_le32(mvm->cfg->dccm2_offset); | ||
1459 | iwl_trans_read_mem_bytes(mvm->trans, mvm->cfg->dccm2_offset, | ||
1460 | dump_mem->data, sram2_len); | ||
1461 | } | ||
1462 | |||
1463 | if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000 && | ||
1464 | CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_B_STEP) { | ||
1465 | dump_data = iwl_fw_error_next_data(dump_data); | ||
1466 | dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); | ||
1467 | dump_data->len = cpu_to_le32(IWL8260_ICCM_LEN + | ||
1468 | sizeof(*dump_mem)); | ||
1469 | dump_mem = (void *)dump_data->data; | ||
1470 | dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM); | ||
1471 | dump_mem->offset = cpu_to_le32(IWL8260_ICCM_OFFSET); | ||
1472 | iwl_trans_read_mem_bytes(mvm->trans, IWL8260_ICCM_OFFSET, | ||
1473 | dump_mem->data, IWL8260_ICCM_LEN); | ||
1474 | } | ||
1475 | |||
1476 | /* Dump fw's virtual image */ | ||
1477 | if (mvm->fw->img[mvm->cur_ucode].paging_mem_size) { | ||
1478 | u32 i; | ||
1479 | |||
1480 | for (i = 1; i < mvm->num_of_paging_blk + 1; i++) { | ||
1481 | struct iwl_fw_error_dump_paging *paging; | ||
1482 | struct page *pages = | ||
1483 | mvm->fw_paging_db[i].fw_paging_block; | ||
1484 | |||
1485 | dump_data = iwl_fw_error_next_data(dump_data); | ||
1486 | dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PAGING); | ||
1487 | dump_data->len = cpu_to_le32(sizeof(*paging) + | ||
1488 | PAGING_BLOCK_SIZE); | ||
1489 | paging = (void *)dump_data->data; | ||
1490 | paging->index = cpu_to_le32(i); | ||
1491 | memcpy(paging->data, page_address(pages), | ||
1492 | PAGING_BLOCK_SIZE); | ||
1493 | } | ||
1494 | } | ||
1495 | |||
1496 | dump_data = iwl_fw_error_next_data(dump_data); | ||
1497 | iwl_dump_prph(mvm->trans, &dump_data); | ||
1498 | |||
1499 | dump_trans_data: | ||
1500 | fw_error_dump->trans_ptr = iwl_trans_dump_data(mvm->trans, | ||
1501 | mvm->fw_dump_trig); | ||
1502 | fw_error_dump->op_mode_len = file_len; | ||
1503 | if (fw_error_dump->trans_ptr) | ||
1504 | file_len += fw_error_dump->trans_ptr->len; | ||
1505 | dump_file->file_len = cpu_to_le32(file_len); | ||
1506 | |||
1507 | dev_coredumpm(mvm->trans->dev, THIS_MODULE, fw_error_dump, 0, | ||
1508 | GFP_KERNEL, iwl_mvm_read_coredump, iwl_mvm_free_coredump); | ||
1509 | |||
1510 | mvm->fw_dump_trig = NULL; | ||
1511 | clear_bit(IWL_MVM_STATUS_DUMPING_FW_LOG, &mvm->status); | ||
1512 | } | ||
1513 | |||
1514 | struct iwl_mvm_dump_desc iwl_mvm_dump_desc_assert = { | ||
1515 | .trig_desc = { | ||
1516 | .type = cpu_to_le32(FW_DBG_TRIGGER_FW_ASSERT), | ||
1517 | }, | ||
1518 | }; | ||
1519 | |||
1520 | static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm) | 944 | static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm) |
1521 | { | 945 | { |
1522 | /* clear the D3 reconfig, we only need it to avoid dumping a | 946 | /* clear the D3 reconfig, we only need it to avoid dumping a |
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 013b37920048..5692856d3e42 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | |||
@@ -1480,69 +1480,10 @@ void iwl_mvm_tdls_ch_switch_work(struct work_struct *work); | |||
1480 | struct ieee80211_vif *iwl_mvm_get_bss_vif(struct iwl_mvm *mvm); | 1480 | struct ieee80211_vif *iwl_mvm_get_bss_vif(struct iwl_mvm *mvm); |
1481 | 1481 | ||
1482 | void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error); | 1482 | void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error); |
1483 | void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm); | ||
1484 | |||
1485 | int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, u8 id); | ||
1486 | int iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm, enum iwl_fw_dbg_trigger trig, | ||
1487 | const char *str, size_t len, | ||
1488 | struct iwl_fw_dbg_trigger_tlv *trigger); | ||
1489 | int iwl_mvm_fw_dbg_collect_desc(struct iwl_mvm *mvm, | ||
1490 | struct iwl_mvm_dump_desc *desc, | ||
1491 | struct iwl_fw_dbg_trigger_tlv *trigger); | ||
1492 | void iwl_mvm_free_fw_dump_desc(struct iwl_mvm *mvm); | ||
1493 | int iwl_mvm_fw_dbg_collect_trig(struct iwl_mvm *mvm, | ||
1494 | struct iwl_fw_dbg_trigger_tlv *trigger, | ||
1495 | const char *fmt, ...) __printf(3, 4); | ||
1496 | unsigned int iwl_mvm_get_wd_timeout(struct iwl_mvm *mvm, | 1483 | unsigned int iwl_mvm_get_wd_timeout(struct iwl_mvm *mvm, |
1497 | struct ieee80211_vif *vif, | 1484 | struct ieee80211_vif *vif, |
1498 | bool tdls, bool cmd_q); | 1485 | bool tdls, bool cmd_q); |
1499 | void iwl_mvm_connection_loss(struct iwl_mvm *mvm, struct ieee80211_vif *vif, | 1486 | void iwl_mvm_connection_loss(struct iwl_mvm *mvm, struct ieee80211_vif *vif, |
1500 | const char *errmsg); | 1487 | const char *errmsg); |
1501 | static inline bool | ||
1502 | iwl_fw_dbg_trigger_vif_match(struct iwl_fw_dbg_trigger_tlv *trig, | ||
1503 | struct ieee80211_vif *vif) | ||
1504 | { | ||
1505 | u32 trig_vif = le32_to_cpu(trig->vif_type); | ||
1506 | |||
1507 | return trig_vif == IWL_FW_DBG_CONF_VIF_ANY || vif->type == trig_vif; | ||
1508 | } | ||
1509 | |||
1510 | static inline bool | ||
1511 | iwl_fw_dbg_trigger_stop_conf_match(struct iwl_mvm *mvm, | ||
1512 | struct iwl_fw_dbg_trigger_tlv *trig) | ||
1513 | { | ||
1514 | return ((trig->mode & IWL_FW_DBG_TRIGGER_STOP) && | ||
1515 | (mvm->fw_dbg_conf == FW_DBG_INVALID || | ||
1516 | (BIT(mvm->fw_dbg_conf) & le32_to_cpu(trig->stop_conf_ids)))); | ||
1517 | } | ||
1518 | |||
1519 | static inline bool | ||
1520 | iwl_fw_dbg_trigger_check_stop(struct iwl_mvm *mvm, | ||
1521 | struct ieee80211_vif *vif, | ||
1522 | struct iwl_fw_dbg_trigger_tlv *trig) | ||
1523 | { | ||
1524 | if (vif && !iwl_fw_dbg_trigger_vif_match(trig, vif)) | ||
1525 | return false; | ||
1526 | |||
1527 | return iwl_fw_dbg_trigger_stop_conf_match(mvm, trig); | ||
1528 | } | ||
1529 | |||
1530 | static inline void | ||
1531 | _iwl_fw_dbg_trigger_simple_stop(struct iwl_mvm *mvm, | ||
1532 | struct ieee80211_vif *vif, | ||
1533 | struct iwl_fw_dbg_trigger_tlv *trigger) | ||
1534 | { | ||
1535 | if (!trigger) | ||
1536 | return; | ||
1537 | |||
1538 | if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trigger)) | ||
1539 | return; | ||
1540 | |||
1541 | iwl_mvm_fw_dbg_collect_trig(mvm, trigger, NULL); | ||
1542 | } | ||
1543 | #define iwl_fw_dbg_trigger_simple_stop(mvm, vif, trig) \ | ||
1544 | _iwl_fw_dbg_trigger_simple_stop((mvm), (vif), \ | ||
1545 | iwl_fw_dbg_get_trigger((mvm)->fw,\ | ||
1546 | (trig))) | ||
1547 | 1488 | ||
1548 | #endif /* __IWL_MVM_H__ */ | 1489 | #endif /* __IWL_MVM_H__ */ |
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index f002e558fc13..f96ed5577c5e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c | |||
@@ -82,6 +82,7 @@ | |||
82 | #include "rs.h" | 82 | #include "rs.h" |
83 | #include "fw-api-scan.h" | 83 | #include "fw-api-scan.h" |
84 | #include "time-event.h" | 84 | #include "time-event.h" |
85 | #include "fw-dbg.h" | ||
85 | 86 | ||
86 | #define DRV_DESCRIPTION "The new Intel(R) wireless AGN driver for Linux" | 87 | #define DRV_DESCRIPTION "The new Intel(R) wireless AGN driver for Linux" |
87 | MODULE_DESCRIPTION(DRV_DESCRIPTION); | 88 | MODULE_DESCRIPTION(DRV_DESCRIPTION); |
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c index 5b58f5320e8d..d1d50ffb1459 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c | |||
@@ -65,6 +65,7 @@ | |||
65 | #include "iwl-trans.h" | 65 | #include "iwl-trans.h" |
66 | #include "mvm.h" | 66 | #include "mvm.h" |
67 | #include "fw-api.h" | 67 | #include "fw-api.h" |
68 | #include "fw-dbg.h" | ||
68 | 69 | ||
69 | /* | 70 | /* |
70 | * iwl_mvm_rx_rx_phy_cmd - REPLY_RX_PHY_CMD handler | 71 | * iwl_mvm_rx_rx_phy_cmd - REPLY_RX_PHY_CMD handler |
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c index 7530eb23035d..52692dfee47a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c | |||
@@ -73,6 +73,7 @@ | |||
73 | #include "mvm.h" | 73 | #include "mvm.h" |
74 | #include "iwl-io.h" | 74 | #include "iwl-io.h" |
75 | #include "iwl-prph.h" | 75 | #include "iwl-prph.h" |
76 | #include "fw-dbg.h" | ||
76 | 77 | ||
77 | /* | 78 | /* |
78 | * For the high priority TE use a time event type that has similar priority to | 79 | * For the high priority TE use a time event type that has similar priority to |
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index c652a66be803..aaebb5d4462a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c | |||
@@ -69,6 +69,7 @@ | |||
69 | #include "iwl-eeprom-parse.h" | 69 | #include "iwl-eeprom-parse.h" |
70 | #include "mvm.h" | 70 | #include "mvm.h" |
71 | #include "sta.h" | 71 | #include "sta.h" |
72 | #include "fw-dbg.h" | ||
72 | 73 | ||
73 | static void | 74 | static void |
74 | iwl_mvm_bar_check_trigger(struct iwl_mvm *mvm, const u8 *addr, | 75 | iwl_mvm_bar_check_trigger(struct iwl_mvm *mvm, const u8 *addr, |
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c index ad0f16909e2e..bbb7f6b27f5e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c | |||
@@ -68,7 +68,7 @@ | |||
68 | #include "iwl-debug.h" | 68 | #include "iwl-debug.h" |
69 | #include "iwl-io.h" | 69 | #include "iwl-io.h" |
70 | #include "iwl-prph.h" | 70 | #include "iwl-prph.h" |
71 | 71 | #include "fw-dbg.h" | |
72 | #include "mvm.h" | 72 | #include "mvm.h" |
73 | #include "fw-api-rs.h" | 73 | #include "fw-api-rs.h" |
74 | 74 | ||