aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLiad Kaufman <liad.kaufman@intel.com>2014-09-16 08:06:54 -0400
committerEmmanuel Grumbach <emmanuel.grumbach@intel.com>2014-12-02 09:50:51 -0500
commit490fefebb6db4c5f80c12ae2c1c6230e24733b2a (patch)
treecd8b107fd058e075deefa5433c8febbcd40d0f92
parentb4c82adcba8cb4b23068a6b800ca98da3bee6888 (diff)
iwlwifi: define the .ucode file format for debug
Debug information can be appended to the firmware file. This information will be used by the driver to enable / disable debugging features in the firmware. Signed-off-by: Liad Kaufman <liad.kaufman@intel.com> Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-drv.c121
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-fw-file.h124
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-fw.h55
3 files changed, 285 insertions, 15 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c
index 2c6e8309ad30..a53b534aa232 100644
--- a/drivers/net/wireless/iwlwifi/iwl-drv.c
+++ b/drivers/net/wireless/iwlwifi/iwl-drv.c
@@ -184,6 +184,11 @@ static void iwl_free_fw_img(struct iwl_drv *drv, struct fw_img *img)
184static void iwl_dealloc_ucode(struct iwl_drv *drv) 184static void iwl_dealloc_ucode(struct iwl_drv *drv)
185{ 185{
186 int i; 186 int i;
187
188 kfree(drv->fw.dbg_dest_tlv);
189 for (i = 0; i < ARRAY_SIZE(drv->fw.dbg_conf_tlv); i++)
190 kfree(drv->fw.dbg_conf_tlv[i]);
191
187 for (i = 0; i < IWL_UCODE_TYPE_MAX; i++) 192 for (i = 0; i < IWL_UCODE_TYPE_MAX; i++)
188 iwl_free_fw_img(drv, drv->fw.img + i); 193 iwl_free_fw_img(drv, drv->fw.img + i);
189} 194}
@@ -308,6 +313,11 @@ struct iwl_firmware_pieces {
308 313
309 u32 init_evtlog_ptr, init_evtlog_size, init_errlog_ptr; 314 u32 init_evtlog_ptr, init_evtlog_size, init_errlog_ptr;
310 u32 inst_evtlog_ptr, inst_evtlog_size, inst_errlog_ptr; 315 u32 inst_evtlog_ptr, inst_evtlog_size, inst_errlog_ptr;
316
317 /* FW debug data parsed for driver usage */
318 struct iwl_fw_dbg_dest_tlv *dbg_dest_tlv;
319 struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_MAX];
320 size_t dbg_conf_tlv_len[FW_DBG_MAX];
311}; 321};
312 322
313/* 323/*
@@ -853,6 +863,58 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
853 capa->n_scan_channels = 863 capa->n_scan_channels =
854 le32_to_cpup((__le32 *)tlv_data); 864 le32_to_cpup((__le32 *)tlv_data);
855 break; 865 break;
866 case IWL_UCODE_TLV_FW_DBG_DEST: {
867 struct iwl_fw_dbg_dest_tlv *dest = (void *)tlv_data;
868
869 if (pieces->dbg_dest_tlv) {
870 IWL_ERR(drv,
871 "dbg destination ignored, already exists\n");
872 break;
873 }
874
875 pieces->dbg_dest_tlv = dest;
876 IWL_INFO(drv, "Found debug destination: %s\n",
877 get_fw_dbg_mode_string(dest->monitor_mode));
878
879 drv->fw.dbg_dest_reg_num =
880 tlv_len - offsetof(struct iwl_fw_dbg_dest_tlv,
881 reg_ops);
882 drv->fw.dbg_dest_reg_num /=
883 sizeof(drv->fw.dbg_dest_tlv->reg_ops[0]);
884
885 break;
886 }
887 case IWL_UCODE_TLV_FW_DBG_CONF: {
888 struct iwl_fw_dbg_conf_tlv *conf = (void *)tlv_data;
889
890 if (!pieces->dbg_dest_tlv) {
891 IWL_ERR(drv,
892 "Ignore dbg config %d - no destination configured\n",
893 conf->id);
894 break;
895 }
896
897 if (conf->id >= ARRAY_SIZE(drv->fw.dbg_conf_tlv)) {
898 IWL_ERR(drv,
899 "Skip unknown configuration: %d\n",
900 conf->id);
901 break;
902 }
903
904 if (pieces->dbg_conf_tlv[conf->id]) {
905 IWL_ERR(drv,
906 "Ignore duplicate dbg config %d\n",
907 conf->id);
908 break;
909 }
910
911 IWL_INFO(drv, "Found debug configuration: %d\n",
912 conf->id);
913
914 pieces->dbg_conf_tlv[conf->id] = conf;
915 pieces->dbg_conf_tlv_len[conf->id] = tlv_len;
916 break;
917 }
856 default: 918 default:
857 IWL_DEBUG_INFO(drv, "unknown TLV: %d\n", tlv_type); 919 IWL_DEBUG_INFO(drv, "unknown TLV: %d\n", tlv_type);
858 break; 920 break;
@@ -996,7 +1058,7 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
996 struct iwl_ucode_header *ucode; 1058 struct iwl_ucode_header *ucode;
997 struct iwlwifi_opmode_table *op; 1059 struct iwlwifi_opmode_table *op;
998 int err; 1060 int err;
999 struct iwl_firmware_pieces pieces; 1061 struct iwl_firmware_pieces *pieces;
1000 const unsigned int api_max = drv->cfg->ucode_api_max; 1062 const unsigned int api_max = drv->cfg->ucode_api_max;
1001 unsigned int api_ok = drv->cfg->ucode_api_ok; 1063 unsigned int api_ok = drv->cfg->ucode_api_ok;
1002 const unsigned int api_min = drv->cfg->ucode_api_min; 1064 const unsigned int api_min = drv->cfg->ucode_api_min;
@@ -1013,7 +1075,9 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
1013 if (!api_ok) 1075 if (!api_ok)
1014 api_ok = api_max; 1076 api_ok = api_max;
1015 1077
1016 memset(&pieces, 0, sizeof(pieces)); 1078 pieces = kzalloc(sizeof(*pieces), GFP_KERNEL);
1079 if (!pieces)
1080 return;
1017 1081
1018 if (!ucode_raw) { 1082 if (!ucode_raw) {
1019 if (drv->fw_index <= api_ok) 1083 if (drv->fw_index <= api_ok)
@@ -1036,10 +1100,10 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
1036 ucode = (struct iwl_ucode_header *)ucode_raw->data; 1100 ucode = (struct iwl_ucode_header *)ucode_raw->data;
1037 1101
1038 if (ucode->ver) 1102 if (ucode->ver)
1039 err = iwl_parse_v1_v2_firmware(drv, ucode_raw, &pieces); 1103 err = iwl_parse_v1_v2_firmware(drv, ucode_raw, pieces);
1040 else 1104 else
1041 err = iwl_parse_tlv_firmware(drv, ucode_raw, &pieces, 1105 err = iwl_parse_tlv_firmware(drv, ucode_raw, pieces,
1042 &fw->ucode_capa); 1106 &fw->ucode_capa);
1043 1107
1044 if (err) 1108 if (err)
1045 goto try_again; 1109 goto try_again;
@@ -1079,7 +1143,7 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
1079 * In mvm uCode there is no difference between data and instructions 1143 * In mvm uCode there is no difference between data and instructions
1080 * sections. 1144 * sections.
1081 */ 1145 */
1082 if (!fw->mvm_fw && validate_sec_sizes(drv, &pieces, drv->cfg)) 1146 if (!fw->mvm_fw && validate_sec_sizes(drv, pieces, drv->cfg))
1083 goto try_again; 1147 goto try_again;
1084 1148
1085 /* Allocate ucode buffers for card's bus-master loading ... */ 1149 /* Allocate ucode buffers for card's bus-master loading ... */
@@ -1088,9 +1152,33 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
1088 * 1) unmodified from disk 1152 * 1) unmodified from disk
1089 * 2) backup cache for save/restore during power-downs */ 1153 * 2) backup cache for save/restore during power-downs */
1090 for (i = 0; i < IWL_UCODE_TYPE_MAX; i++) 1154 for (i = 0; i < IWL_UCODE_TYPE_MAX; i++)
1091 if (iwl_alloc_ucode(drv, &pieces, i)) 1155 if (iwl_alloc_ucode(drv, pieces, i))
1092 goto out_free_fw; 1156 goto out_free_fw;
1093 1157
1158 if (pieces->dbg_dest_tlv) {
1159 drv->fw.dbg_dest_tlv =
1160 kmemdup(pieces->dbg_dest_tlv,
1161 sizeof(*pieces->dbg_dest_tlv) +
1162 sizeof(pieces->dbg_dest_tlv->reg_ops[0]) *
1163 drv->fw.dbg_dest_reg_num, GFP_KERNEL);
1164
1165 if (!drv->fw.dbg_dest_tlv)
1166 goto out_free_fw;
1167 }
1168
1169 for (i = 0; i < ARRAY_SIZE(drv->fw.dbg_conf_tlv); i++) {
1170 if (pieces->dbg_conf_tlv[i]) {
1171 drv->fw.dbg_conf_tlv_len[i] =
1172 pieces->dbg_conf_tlv_len[i];
1173 drv->fw.dbg_conf_tlv[i] =
1174 kmemdup(pieces->dbg_conf_tlv[i],
1175 drv->fw.dbg_conf_tlv_len[i],
1176 GFP_KERNEL);
1177 if (!drv->fw.dbg_conf_tlv[i])
1178 goto out_free_fw;
1179 }
1180 }
1181
1094 /* Now that we can no longer fail, copy information */ 1182 /* Now that we can no longer fail, copy information */
1095 1183
1096 /* 1184 /*
@@ -1098,20 +1186,20 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
1098 * for each event, which is of mode 1 (including timestamp) for all 1186 * for each event, which is of mode 1 (including timestamp) for all
1099 * new microcodes that include this information. 1187 * new microcodes that include this information.
1100 */ 1188 */
1101 fw->init_evtlog_ptr = pieces.init_evtlog_ptr; 1189 fw->init_evtlog_ptr = pieces->init_evtlog_ptr;
1102 if (pieces.init_evtlog_size) 1190 if (pieces->init_evtlog_size)
1103 fw->init_evtlog_size = (pieces.init_evtlog_size - 16)/12; 1191 fw->init_evtlog_size = (pieces->init_evtlog_size - 16)/12;
1104 else 1192 else
1105 fw->init_evtlog_size = 1193 fw->init_evtlog_size =
1106 drv->cfg->base_params->max_event_log_size; 1194 drv->cfg->base_params->max_event_log_size;
1107 fw->init_errlog_ptr = pieces.init_errlog_ptr; 1195 fw->init_errlog_ptr = pieces->init_errlog_ptr;
1108 fw->inst_evtlog_ptr = pieces.inst_evtlog_ptr; 1196 fw->inst_evtlog_ptr = pieces->inst_evtlog_ptr;
1109 if (pieces.inst_evtlog_size) 1197 if (pieces->inst_evtlog_size)
1110 fw->inst_evtlog_size = (pieces.inst_evtlog_size - 16)/12; 1198 fw->inst_evtlog_size = (pieces->inst_evtlog_size - 16)/12;
1111 else 1199 else
1112 fw->inst_evtlog_size = 1200 fw->inst_evtlog_size =
1113 drv->cfg->base_params->max_event_log_size; 1201 drv->cfg->base_params->max_event_log_size;
1114 fw->inst_errlog_ptr = pieces.inst_errlog_ptr; 1202 fw->inst_errlog_ptr = pieces->inst_errlog_ptr;
1115 1203
1116 /* 1204 /*
1117 * figure out the offset of chain noise reset and gain commands 1205 * figure out the offset of chain noise reset and gain commands
@@ -1213,10 +1301,12 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
1213 iwl_req_fw_callback(ucode_raw, context); 1301 iwl_req_fw_callback(ucode_raw, context);
1214 } 1302 }
1215 1303
1304 kfree(pieces);
1216 return; 1305 return;
1217 1306
1218 try_again: 1307 try_again:
1219 /* try next, if any */ 1308 /* try next, if any */
1309 kfree(pieces);
1220 release_firmware(ucode_raw); 1310 release_firmware(ucode_raw);
1221 if (iwl_request_firmware(drv, false)) 1311 if (iwl_request_firmware(drv, false))
1222 goto out_unbind; 1312 goto out_unbind;
@@ -1227,6 +1317,7 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
1227 iwl_dealloc_ucode(drv); 1317 iwl_dealloc_ucode(drv);
1228 release_firmware(ucode_raw); 1318 release_firmware(ucode_raw);
1229 out_unbind: 1319 out_unbind:
1320 kfree(pieces);
1230 complete(&drv->request_firmware_complete); 1321 complete(&drv->request_firmware_complete);
1231 device_release_driver(drv->trans->dev); 1322 device_release_driver(drv->trans->dev);
1232} 1323}
diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/iwlwifi/iwl-fw-file.h
index df5613a8be15..9faad691c13c 100644
--- a/drivers/net/wireless/iwlwifi/iwl-fw-file.h
+++ b/drivers/net/wireless/iwlwifi/iwl-fw-file.h
@@ -131,6 +131,8 @@ enum iwl_ucode_tlv_type {
131 IWL_UCODE_TLV_API_CHANGES_SET = 29, 131 IWL_UCODE_TLV_API_CHANGES_SET = 29,
132 IWL_UCODE_TLV_ENABLED_CAPABILITIES = 30, 132 IWL_UCODE_TLV_ENABLED_CAPABILITIES = 30,
133 IWL_UCODE_TLV_N_SCAN_CHANNELS = 31, 133 IWL_UCODE_TLV_N_SCAN_CHANNELS = 31,
134 IWL_UCODE_TLV_FW_DBG_DEST = 38,
135 IWL_UCODE_TLV_FW_DBG_CONF = 39,
134}; 136};
135 137
136struct iwl_ucode_tlv { 138struct iwl_ucode_tlv {
@@ -362,4 +364,126 @@ struct iwl_fw_cipher_scheme {
362 u8 hw_cipher; 364 u8 hw_cipher;
363} __packed; 365} __packed;
364 366
367enum iwl_fw_dbg_reg_operator {
368 CSR_ASSIGN,
369 CSR_SETBIT,
370 CSR_CLEARBIT,
371
372 PRPH_ASSIGN,
373 PRPH_SETBIT,
374 PRPH_CLEARBIT,
375};
376
377/**
378 * struct iwl_fw_dbg_reg_op - an operation on a register
379 *
380 * @op: %enum iwl_fw_dbg_reg_operator
381 * @addr: offset of the register
382 * @val: value
383 */
384struct iwl_fw_dbg_reg_op {
385 u8 op;
386 u8 reserved[3];
387 __le32 addr;
388 __le32 val;
389} __packed;
390
391/**
392 * enum iwl_fw_dbg_monitor_mode - available monitor recording modes
393 *
394 * @SMEM_MODE: monitor stores the data in SMEM
395 * @EXTERNAL_MODE: monitor stores the data in allocated DRAM
396 * @MARBH_MODE: monitor stores the data in MARBH buffer
397 */
398enum iwl_fw_dbg_monitor_mode {
399 SMEM_MODE = 0,
400 EXTERNAL_MODE = 1,
401 MARBH_MODE = 2,
402};
403
404/**
405 * struct iwl_fw_dbg_dest_tlv - configures the destination of the debug data
406 *
407 * @version: version of the TLV - currently 0
408 * @monitor_mode: %enum iwl_fw_dbg_monitor_mode
409 * @base_reg: addr of the base addr register (PRPH)
410 * @end_reg: addr of the end addr register (PRPH)
411 * @write_ptr_reg: the addr of the reg of the write pointer
412 * @wrap_count: the addr of the reg of the wrap_count
413 * @base_shift: shift right of the base addr reg
414 * @end_shift: shift right of the end addr reg
415 * @reg_ops: array of registers operations
416 *
417 * This parses IWL_UCODE_TLV_FW_DBG_DEST
418 */
419struct iwl_fw_dbg_dest_tlv {
420 u8 version;
421 u8 monitor_mode;
422 u8 reserved[2];
423 __le32 base_reg;
424 __le32 end_reg;
425 __le32 write_ptr_reg;
426 __le32 wrap_count;
427 u8 base_shift;
428 u8 end_shift;
429 struct iwl_fw_dbg_reg_op reg_ops[0];
430} __packed;
431
432struct iwl_fw_dbg_conf_hcmd {
433 u8 id;
434 u8 reserved;
435 __le16 len;
436 u8 data[0];
437} __packed;
438
439/**
440 * struct iwl_fw_dbg_trigger - a TLV that describes a debug configuration
441 *
442 * @enabled: is this trigger enabled
443 * @reserved:
444 * @len: length, in bytes, of the %trigger field
445 * @trigger: pointer to a trigger struct
446 */
447struct iwl_fw_dbg_trigger {
448 u8 enabled;
449 u8 reserved;
450 u8 len;
451 u8 trigger[0];
452} __packed;
453
454/**
455 * enum iwl_fw_dbg_conf - configurations available
456 *
457 * @FW_DBG_CUSTOM: take this configuration from alive
458 * Note that the trigger is NO-OP for this configuration
459 */
460enum iwl_fw_dbg_conf {
461 FW_DBG_CUSTOM = 0,
462
463 /* must be last */
464 FW_DBG_MAX,
465 FW_DBG_INVALID = 0xff,
466};
467
468/**
469 * struct iwl_fw_dbg_conf_tlv - a TLV that describes a debug configuration
470 *
471 * @id: %enum iwl_fw_dbg_conf
472 * @usniffer: should the uSniffer image be used
473 * @num_of_hcmds: how many HCMDs to send are present here
474 * @hcmd: a variable length host command to be sent to apply the configuration.
475 * If there is more than one HCMD to send, they will appear one after the
476 * other and be sent in the order that they appear in.
477 * This parses IWL_UCODE_TLV_FW_DBG_CONF
478 */
479struct iwl_fw_dbg_conf_tlv {
480 u8 id;
481 u8 usniffer;
482 u8 reserved;
483 u8 num_of_hcmds;
484 struct iwl_fw_dbg_conf_hcmd hcmd;
485
486 /* struct iwl_fw_dbg_trigger sits after all variable length hcmds */
487} __packed;
488
365#endif /* __iwl_fw_file_h__ */ 489#endif /* __iwl_fw_file_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-fw.h b/drivers/net/wireless/iwlwifi/iwl-fw.h
index eaf1ff04adb9..20d44ea315ea 100644
--- a/drivers/net/wireless/iwlwifi/iwl-fw.h
+++ b/drivers/net/wireless/iwlwifi/iwl-fw.h
@@ -150,6 +150,10 @@ struct iwl_fw_cscheme_list {
150 * @mvm_fw: indicates this is MVM firmware 150 * @mvm_fw: indicates this is MVM firmware
151 * @cipher_scheme: optional external cipher scheme. 151 * @cipher_scheme: optional external cipher scheme.
152 * @human_readable: human readable version 152 * @human_readable: human readable version
153 * @dbg_dest_tlv: points to the destination TLV for debug
154 * @dbg_conf_tlv: array of pointers to configuration TLVs for debug
155 * @dbg_conf_tlv_len: lengths of the @dbg_conf_tlv entries
156 * @dbg_dest_reg_num: num of reg_ops in %dbg_dest_tlv
153 */ 157 */
154struct iwl_fw { 158struct iwl_fw {
155 u32 ucode_ver; 159 u32 ucode_ver;
@@ -174,6 +178,57 @@ struct iwl_fw {
174 178
175 struct ieee80211_cipher_scheme cs[IWL_UCODE_MAX_CS]; 179 struct ieee80211_cipher_scheme cs[IWL_UCODE_MAX_CS];
176 u8 human_readable[FW_VER_HUMAN_READABLE_SZ]; 180 u8 human_readable[FW_VER_HUMAN_READABLE_SZ];
181
182 struct iwl_fw_dbg_dest_tlv *dbg_dest_tlv;
183 struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_MAX];
184 size_t dbg_conf_tlv_len[FW_DBG_MAX];
185
186 u8 dbg_dest_reg_num;
177}; 187};
178 188
189static inline const char *get_fw_dbg_mode_string(int mode)
190{
191 switch (mode) {
192 case SMEM_MODE:
193 return "SMEM";
194 case EXTERNAL_MODE:
195 return "EXTERNAL_DRAM";
196 case MARBH_MODE:
197 return "MARBH";
198 default:
199 return "UNKNOWN";
200 }
201}
202
203static inline const struct iwl_fw_dbg_trigger *
204iwl_fw_dbg_conf_get_trigger(const struct iwl_fw *fw, u8 id)
205{
206 const struct iwl_fw_dbg_conf_tlv *conf_tlv = fw->dbg_conf_tlv[id];
207 u8 *ptr;
208 int i;
209
210 if (!conf_tlv)
211 return NULL;
212
213 ptr = (void *)&conf_tlv->hcmd;
214 for (i = 0; i < conf_tlv->num_of_hcmds; i++) {
215 ptr += sizeof(conf_tlv->hcmd);
216 ptr += le16_to_cpu(conf_tlv->hcmd.len);
217 }
218
219 return (const struct iwl_fw_dbg_trigger *)ptr;
220}
221
222static inline bool
223iwl_fw_dbg_conf_enabled(const struct iwl_fw *fw, u8 id)
224{
225 const struct iwl_fw_dbg_trigger *trigger =
226 iwl_fw_dbg_conf_get_trigger(fw, id);
227
228 if (!trigger)
229 return false;
230
231 return trigger->enabled;
232}
233
179#endif /* __iwl_fw_h__ */ 234#endif /* __iwl_fw_h__ */