diff options
Diffstat (limited to 'drivers/net/wireless/intel/iwlwifi/fw/dbg.c')
| -rw-r--r-- | drivers/net/wireless/intel/iwlwifi/fw/dbg.c | 1155 | 
1 files changed, 367 insertions, 788 deletions
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c index 4d81776f576d..5c8602de9168 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c @@ -243,7 +243,7 @@ static void iwl_fw_dump_rxf(struct iwl_fw_runtime *fwrt,  		/* Pull RXF2 */  		iwl_fwrt_dump_rxf(fwrt, dump_data, cfg->rxfifo2_size,  				  RXF_DIFF_FROM_PREV + -				  fwrt->trans->cfg->umac_prph_offset, 1); +				  fwrt->trans->trans_cfg->umac_prph_offset, 1);  		/* Pull LMAC2 RXF1 */  		if (fwrt->smem_cfg.num_lmacs > 1)  			iwl_fwrt_dump_rxf(fwrt, dump_data, @@ -468,6 +468,9 @@ static const struct iwl_prph_range iwl_prph_dump_addr_9000[] = {  	{ .start = 0x00a05400, .end = 0x00a056e8 },  	{ .start = 0x00a08000, .end = 0x00a098bc },  	{ .start = 0x00a02400, .end = 0x00a02758 }, +	{ .start = 0x00a04764, .end = 0x00a0476c }, +	{ .start = 0x00a04770, .end = 0x00a04774 }, +	{ .start = 0x00a04620, .end = 0x00a04624 },  };  static const struct iwl_prph_range iwl_prph_dump_addr_22000[] = { @@ -681,17 +684,18 @@ static void iwl_fw_prph_handler(struct iwl_fw_runtime *fwrt, void *ptr,  {  	u32 range_len; -	if (fwrt->trans->cfg->device_family >= IWL_DEVICE_FAMILY_AX210) { +	if (fwrt->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) {  		range_len = ARRAY_SIZE(iwl_prph_dump_addr_ax210);  		handler(fwrt, iwl_prph_dump_addr_ax210, range_len, ptr); -	} else if (fwrt->trans->cfg->device_family >= IWL_DEVICE_FAMILY_22000) { +	} else if (fwrt->trans->trans_cfg->device_family >= +		   IWL_DEVICE_FAMILY_22000) {  		range_len = ARRAY_SIZE(iwl_prph_dump_addr_22000);  		handler(fwrt, iwl_prph_dump_addr_22000, range_len, ptr);  	} else {  		range_len = ARRAY_SIZE(iwl_prph_dump_addr_comm);  		handler(fwrt, iwl_prph_dump_addr_comm, range_len, ptr); -		if (fwrt->trans->cfg->mq_rx_supported) { +		if (fwrt->trans->trans_cfg->mq_rx_supported) {  			range_len = ARRAY_SIZE(iwl_prph_dump_addr_9000);  			handler(fwrt, iwl_prph_dump_addr_9000, range_len, ptr);  		} @@ -853,7 +857,8 @@ iwl_fw_error_dump_file(struct iwl_fw_runtime *fwrt,  			iwl_fw_prph_handler(fwrt, &prph_len,  					    iwl_fw_get_prph_len); -		if (fwrt->trans->cfg->device_family == IWL_DEVICE_FAMILY_7000 && +		if (fwrt->trans->trans_cfg->device_family == +		    IWL_DEVICE_FAMILY_7000 &&  		    iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_RADIO_REG))  			radio_len = sizeof(*dump_data) + RADIO_REG_MAX_READ;  	} @@ -1103,25 +1108,9 @@ static int iwl_dump_ini_dev_mem_iter(struct iwl_fw_runtime *fwrt,  	return sizeof(*range) + le32_to_cpu(range->range_data_size);  } -static int -iwl_dump_ini_paging_gen2_iter(struct iwl_fw_runtime *fwrt, -			      struct iwl_fw_ini_region_cfg *reg, -			      void *range_ptr, int idx) -{ -	struct iwl_fw_ini_error_dump_range *range = range_ptr; -	u32 page_size = fwrt->trans->init_dram.paging[idx].size; - -	range->page_num = cpu_to_le32(idx); -	range->range_data_size = cpu_to_le32(page_size); -	memcpy(range->data, fwrt->trans->init_dram.paging[idx].block, -	       page_size); - -	return sizeof(*range) + le32_to_cpu(range->range_data_size); -} - -static int iwl_dump_ini_paging_iter(struct iwl_fw_runtime *fwrt, -				    struct iwl_fw_ini_region_cfg *reg, -				    void *range_ptr, int idx) +static int _iwl_dump_ini_paging_iter(struct iwl_fw_runtime *fwrt, +				     struct iwl_fw_ini_region_cfg *reg, +				     void *range_ptr, int idx)  {  	/* increase idx by 1 since the pages are from 1 to  	 * fwrt->num_of_paging_blk + 1 @@ -1142,6 +1131,27 @@ static int iwl_dump_ini_paging_iter(struct iwl_fw_runtime *fwrt,  	return sizeof(*range) + le32_to_cpu(range->range_data_size);  } +static int iwl_dump_ini_paging_iter(struct iwl_fw_runtime *fwrt, +				    struct iwl_fw_ini_region_cfg *reg, +				    void *range_ptr, int idx) +{ +	struct iwl_fw_ini_error_dump_range *range; +	u32 page_size; + +	if (!fwrt->trans->trans_cfg->gen2) +		return _iwl_dump_ini_paging_iter(fwrt, reg, range_ptr, idx); + +	range = range_ptr; +	page_size = fwrt->trans->init_dram.paging[idx].size; + +	range->page_num = cpu_to_le32(idx); +	range->range_data_size = cpu_to_le32(page_size); +	memcpy(range->data, fwrt->trans->init_dram.paging[idx].block, +	       page_size); + +	return sizeof(*range) + le32_to_cpu(range->range_data_size); +} +  static int  iwl_dump_ini_mon_dram_iter(struct iwl_fw_runtime *fwrt,  			   struct iwl_fw_ini_region_cfg *reg, void *range_ptr, @@ -1163,35 +1173,23 @@ iwl_dump_ini_mon_dram_iter(struct iwl_fw_runtime *fwrt,  	return sizeof(*range) + le32_to_cpu(range->range_data_size);  } -struct iwl_ini_txf_iter_data { -	int fifo; -	int lmac; -	u32 fifo_size; -	bool internal_txf; -	bool init; -}; -  static bool iwl_ini_txf_iter(struct iwl_fw_runtime *fwrt, -			     struct iwl_fw_ini_region_cfg *reg) +			     struct iwl_fw_ini_region_cfg *reg, int idx)  { -	struct iwl_ini_txf_iter_data *iter = fwrt->dump.fifo_iter; +	struct iwl_txf_iter_data *iter = &fwrt->dump.txf_iter_data;  	struct iwl_fwrt_shared_mem_cfg *cfg = &fwrt->smem_cfg;  	int txf_num = cfg->num_txfifo_entries;  	int int_txf_num = ARRAY_SIZE(cfg->internal_txfifo_size);  	u32 lmac_bitmap = le32_to_cpu(reg->fifos.fid1); -	if (!iter) -		return false; - -	if (iter->init) { +	if (!idx) {  		if (le32_to_cpu(reg->offset) &&  		    WARN_ONCE(cfg->num_lmacs == 1,  			      "Invalid lmac offset: 0x%x\n",  			      le32_to_cpu(reg->offset)))  			return false; -		iter->init = false; -		iter->internal_txf = false; +		iter->internal_txf = 0;  		iter->fifo_size = 0;  		iter->fifo = -1;  		if (le32_to_cpu(reg->offset)) @@ -1208,7 +1206,7 @@ static bool iwl_ini_txf_iter(struct iwl_fw_runtime *fwrt,  				return true;  		} -	iter->internal_txf = true; +	iter->internal_txf = 1;  	if (!fw_has_capa(&fwrt->fw->ucode_capa,  			 IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG)) @@ -1229,7 +1227,7 @@ static int iwl_dump_ini_txf_iter(struct iwl_fw_runtime *fwrt,  				 void *range_ptr, int idx)  {  	struct iwl_fw_ini_error_dump_range *range = range_ptr; -	struct iwl_ini_txf_iter_data *iter; +	struct iwl_txf_iter_data *iter = &fwrt->dump.txf_iter_data;  	struct iwl_fw_ini_error_dump_register *reg_dump = (void *)range->data;  	u32 offs = le32_to_cpu(reg->offset), addr;  	u32 registers_size = @@ -1238,14 +1236,12 @@ static int iwl_dump_ini_txf_iter(struct iwl_fw_runtime *fwrt,  	unsigned long flags;  	int i; -	if (!iwl_ini_txf_iter(fwrt, reg)) +	if (!iwl_ini_txf_iter(fwrt, reg, idx))  		return -EIO;  	if (!iwl_trans_grab_nic_access(fwrt->trans, &flags))  		return -EBUSY; -	iter = fwrt->dump.fifo_iter; -  	range->fifo_hdr.fifo_num = cpu_to_le32(iter->fifo);  	range->fifo_hdr.num_of_registers = reg->fifos.num_of_registers;  	range->range_data_size = cpu_to_le32(iter->fifo_size + registers_size); @@ -1448,7 +1444,7 @@ static void  	struct iwl_fw_ini_monitor_dump *mon_dump = (void *)data;  	u32 write_ptr_addr, write_ptr_msk, cycle_cnt_addr, cycle_cnt_msk; -	switch (fwrt->trans->cfg->device_family) { +	switch (fwrt->trans->trans_cfg->device_family) {  	case IWL_DEVICE_FAMILY_9000:  	case IWL_DEVICE_FAMILY_22000:  		write_ptr_addr = MON_BUFF_WRPTR_VER2; @@ -1458,7 +1454,7 @@ static void  		break;  	default:  		IWL_ERR(fwrt, "Unsupported device family %d\n", -			fwrt->trans->cfg->device_family); +			fwrt->trans->trans_cfg->device_family);  		return NULL;  	} @@ -1475,10 +1471,10 @@ static void  	struct iwl_fw_ini_monitor_dump *mon_dump = (void *)data;  	const struct iwl_cfg *cfg = fwrt->trans->cfg; -	if (fwrt->trans->cfg->device_family != IWL_DEVICE_FAMILY_9000 && -	    fwrt->trans->cfg->device_family != IWL_DEVICE_FAMILY_22000) { +	if (fwrt->trans->trans_cfg->device_family != IWL_DEVICE_FAMILY_9000 && +	    fwrt->trans->trans_cfg->device_family != IWL_DEVICE_FAMILY_22000) {  		IWL_ERR(fwrt, "Unsupported device family %d\n", -			fwrt->trans->cfg->device_family); +			fwrt->trans->trans_cfg->device_family);  		return NULL;  	} @@ -1496,15 +1492,12 @@ static u32 iwl_dump_ini_mem_ranges(struct iwl_fw_runtime *fwrt,  	return le32_to_cpu(reg->internal.num_of_ranges);  } -static u32 iwl_dump_ini_paging_gen2_ranges(struct iwl_fw_runtime *fwrt, -					   struct iwl_fw_ini_region_cfg *reg) -{ -	return fwrt->trans->init_dram.paging_cnt; -} -  static u32 iwl_dump_ini_paging_ranges(struct iwl_fw_runtime *fwrt,  				      struct iwl_fw_ini_region_cfg *reg)  { +	if (fwrt->trans->trans_cfg->gen2) +		return fwrt->trans->init_dram.paging_cnt; +  	return fwrt->num_of_paging_blk;  } @@ -1517,16 +1510,11 @@ static u32 iwl_dump_ini_mon_dram_ranges(struct iwl_fw_runtime *fwrt,  static u32 iwl_dump_ini_txf_ranges(struct iwl_fw_runtime *fwrt,  				   struct iwl_fw_ini_region_cfg *reg)  { -	struct iwl_ini_txf_iter_data iter = { .init = true }; -	void *fifo_iter = fwrt->dump.fifo_iter;  	u32 num_of_fifos = 0; -	fwrt->dump.fifo_iter = &iter; -	while (iwl_ini_txf_iter(fwrt, reg)) +	while (iwl_ini_txf_iter(fwrt, reg, num_of_fifos))  		num_of_fifos++; -	fwrt->dump.fifo_iter = fifo_iter; -  	return num_of_fifos;  } @@ -1548,20 +1536,6 @@ static u32 iwl_dump_ini_mem_get_size(struct iwl_fw_runtime *fwrt,  		 le32_to_cpu(reg->internal.range_data_size));  } -static u32 iwl_dump_ini_paging_gen2_get_size(struct iwl_fw_runtime *fwrt, -					     struct iwl_fw_ini_region_cfg *reg) -{ -	int i; -	u32 range_header_len = sizeof(struct iwl_fw_ini_error_dump_range); -	u32 size = sizeof(struct iwl_fw_ini_error_dump); - -	for (i = 0; i < iwl_dump_ini_paging_gen2_ranges(fwrt, reg); i++) -		size += range_header_len + -			fwrt->trans->init_dram.paging[i].size; - -	return size; -} -  static u32 iwl_dump_ini_paging_get_size(struct iwl_fw_runtime *fwrt,  					struct iwl_fw_ini_region_cfg *reg)  { @@ -1569,8 +1543,15 @@ static u32 iwl_dump_ini_paging_get_size(struct iwl_fw_runtime *fwrt,  	u32 range_header_len = sizeof(struct iwl_fw_ini_error_dump_range);  	u32 size = sizeof(struct iwl_fw_ini_error_dump); -	for (i = 1; i <= iwl_dump_ini_paging_ranges(fwrt, reg); i++) -		size += range_header_len + fwrt->fw_paging_db[i].fw_paging_size; +	if (fwrt->trans->trans_cfg->gen2) { +		for (i = 0; i < iwl_dump_ini_paging_ranges(fwrt, reg); i++) +			size += range_header_len + +				fwrt->trans->init_dram.paging[i].size; +	} else { +		for (i = 1; i <= iwl_dump_ini_paging_ranges(fwrt, reg); i++) +			size += range_header_len + +				fwrt->fw_paging_db[i].fw_paging_size; +	}  	return size;  } @@ -1599,25 +1580,21 @@ static u32 iwl_dump_ini_mon_smem_get_size(struct iwl_fw_runtime *fwrt,  static u32 iwl_dump_ini_txf_get_size(struct iwl_fw_runtime *fwrt,  				     struct iwl_fw_ini_region_cfg *reg)  { -	struct iwl_ini_txf_iter_data iter = { .init = true }; -	void *fifo_iter = fwrt->dump.fifo_iter; +	struct iwl_txf_iter_data *iter = &fwrt->dump.txf_iter_data;  	u32 size = 0;  	u32 fifo_hdr = sizeof(struct iwl_fw_ini_error_dump_range) +  		le32_to_cpu(reg->fifos.num_of_registers) *  		sizeof(struct iwl_fw_ini_error_dump_register); -	fwrt->dump.fifo_iter = &iter; -	while (iwl_ini_txf_iter(fwrt, reg)) { +	while (iwl_ini_txf_iter(fwrt, reg, size)) {  		size += fifo_hdr;  		if (!reg->fifos.header_only) -			size += iter.fifo_size; +			size += iter->fifo_size;  	}  	if (size)  		size += sizeof(struct iwl_fw_ini_error_dump); -	fwrt->dump.fifo_iter = fifo_iter; -  	return size;  } @@ -1661,38 +1638,50 @@ struct iwl_dump_ini_mem_ops {  };  /** - * iwl_dump_ini_mem - copy a memory region into the dump - * @fwrt: fw runtime struct. - * @data: dump memory data. - * @reg: region to copy to the dump. - * @ops: memory dump operations. + * iwl_dump_ini_mem + * + * Creates a dump tlv and copy a memory region into it. + * Returns the size of the current dump tlv or 0 if failed + * + * @fwrt: fw runtime struct + * @list: list to add the dump tlv to + * @reg: memory region + * @ops: memory dump operations   */ -static void -iwl_dump_ini_mem(struct iwl_fw_runtime *fwrt, -		 struct iwl_fw_error_dump_data **data, -		 struct iwl_fw_ini_region_cfg *reg, -		 struct iwl_dump_ini_mem_ops *ops) +static u32 iwl_dump_ini_mem(struct iwl_fw_runtime *fwrt, struct list_head *list, +			    struct iwl_fw_ini_region_cfg *reg, +			    const struct iwl_dump_ini_mem_ops *ops)  { -	struct iwl_fw_ini_error_dump_header *header = (void *)(*data)->data; +	struct iwl_fw_ini_dump_entry *entry; +	struct iwl_fw_error_dump_data *tlv; +	struct iwl_fw_ini_error_dump_header *header;  	u32 num_of_ranges, i, type = le32_to_cpu(reg->region_type), size;  	void *range; -	if (WARN_ON(!ops || !ops->get_num_of_ranges || !ops->get_size || -		    !ops->fill_mem_hdr || !ops->fill_range)) -		return; +	if (!ops->get_num_of_ranges || !ops->get_size || !ops->fill_mem_hdr || +	    !ops->fill_range) +		return 0;  	size = ops->get_size(fwrt, reg);  	if (!size) -		return; +		return 0; + +	entry = kmalloc(sizeof(*entry) + sizeof(*tlv) + size, GFP_KERNEL); +	if (!entry) +		return 0; + +	entry->size = sizeof(*tlv) + size; -	IWL_DEBUG_FW(fwrt, "WRT: collecting region: id=%d, type=%d\n", +	tlv = (void *)entry->data; +	tlv->type = cpu_to_le32(type); +	tlv->len = cpu_to_le32(size); + +	IWL_DEBUG_FW(fwrt, "WRT: Collecting region: id=%d, type=%d\n",  		     le32_to_cpu(reg->region_id), type);  	num_of_ranges = ops->get_num_of_ranges(fwrt, reg); -	(*data)->type = cpu_to_le32(type); -	(*data)->len = cpu_to_le32(size); - +	header = (void *)tlv->data;  	header->region_id = reg->region_id;  	header->num_of_ranges = cpu_to_le32(num_of_ranges);  	header->name_len = cpu_to_le32(min_t(int, IWL_FW_INI_MAX_NAME, @@ -1702,10 +1691,9 @@ iwl_dump_ini_mem(struct iwl_fw_runtime *fwrt,  	range = ops->fill_mem_hdr(fwrt, reg, header);  	if (!range) {  		IWL_ERR(fwrt, -			"WRT: failed to fill region header: id=%d, type=%d\n", +			"WRT: Failed to fill region header: id=%d, type=%d\n",  			le32_to_cpu(reg->region_id), type); -		memset(*data, 0, size); -		return; +		goto out_err;  	}  	for (i = 0; i < num_of_ranges; i++) { @@ -1713,30 +1701,49 @@ iwl_dump_ini_mem(struct iwl_fw_runtime *fwrt,  		if (range_size < 0) {  			IWL_ERR(fwrt, -				"WRT: failed to dump region: id=%d, type=%d\n", +				"WRT: Failed to dump region: id=%d, type=%d\n",  				le32_to_cpu(reg->region_id), type); -			memset(*data, 0, size); -			return; +			goto out_err;  		}  		range = range + range_size;  	} -	*data = iwl_fw_error_next_data(*data); + +	list_add_tail(&entry->list, list); + +	return entry->size; + +out_err: +	kfree(entry); + +	return 0;  } -static void iwl_dump_ini_info(struct iwl_fw_runtime *fwrt, -			      struct iwl_fw_ini_trigger *trigger, -			      struct iwl_fw_error_dump_data **data) +static u32 iwl_dump_ini_info(struct iwl_fw_runtime *fwrt, +			     struct iwl_fw_ini_trigger *trigger, +			     struct list_head *list)  { -	struct iwl_fw_ini_dump_info *dump = (void *)(*data)->data; +	struct iwl_fw_ini_dump_entry *entry; +	struct iwl_fw_error_dump_data *tlv; +	struct iwl_fw_ini_dump_info *dump;  	u32 reg_ids_size = le32_to_cpu(trigger->num_regions) * sizeof(__le32); +	u32 size = sizeof(*tlv) + sizeof(*dump) + reg_ids_size; -	(*data)->type = cpu_to_le32(IWL_INI_DUMP_INFO_TYPE); -	(*data)->len = cpu_to_le32(sizeof(*dump) + reg_ids_size); +	entry = kmalloc(sizeof(*entry) + size, GFP_KERNEL); +	if (!entry) +		return 0; + +	entry->size = size; + +	tlv = (void *)entry->data; +	tlv->type = cpu_to_le32(IWL_INI_DUMP_INFO_TYPE); +	tlv->len = cpu_to_le32(sizeof(*dump) + reg_ids_size); + +	dump = (void *)tlv->data;  	dump->version = cpu_to_le32(IWL_INI_DUMP_VER);  	dump->trigger_id = trigger->trigger_id;  	dump->is_external_cfg = -		cpu_to_le32(fwrt->trans->dbg.external_ini_loaded); +		cpu_to_le32(fwrt->trans->dbg.external_ini_cfg);  	dump->ver_type = cpu_to_le32(fwrt->dump.fw_ver.type);  	dump->ver_subtype = cpu_to_le32(fwrt->dump.fw_ver.subtype); @@ -1770,30 +1777,98 @@ static void iwl_dump_ini_info(struct iwl_fw_runtime *fwrt,  	dump->external_dbg_cfg_name_len =  		cpu_to_le32(sizeof(dump->external_dbg_cfg_name)); -	/* dump info size is allocated in iwl_fw_ini_get_trigger_len. -	 * The driver allocates (sizeof(*dump) + reg_ids_size) so it is safe to -	 * use reg_ids_size -	 */  	memcpy(dump->external_dbg_cfg_name, fwrt->dump.external_dbg_cfg_name,  	       sizeof(dump->external_dbg_cfg_name));  	dump->regions_num = trigger->num_regions;  	memcpy(dump->region_ids, trigger->data, reg_ids_size); -	*data = iwl_fw_error_next_data(*data); +	/* add dump info TLV to the beginning of the list since it needs to be +	 * the first TLV in the dump +	 */ +	list_add(&entry->list, list); + +	return entry->size;  } -static int iwl_fw_ini_get_trigger_len(struct iwl_fw_runtime *fwrt, -				      struct iwl_fw_ini_trigger *trigger) -{ -	int i, ret_size = 0, hdr_len = sizeof(struct iwl_fw_error_dump_data); -	u32 size; +static const struct iwl_dump_ini_mem_ops iwl_dump_ini_region_ops[] = { +	[IWL_FW_INI_REGION_INVALID] = {}, +	[IWL_FW_INI_REGION_DEVICE_MEMORY] = { +		.get_num_of_ranges = iwl_dump_ini_mem_ranges, +		.get_size = iwl_dump_ini_mem_get_size, +		.fill_mem_hdr = iwl_dump_ini_mem_fill_header, +		.fill_range = iwl_dump_ini_dev_mem_iter, +	}, +	[IWL_FW_INI_REGION_PERIPHERY_MAC] = { +		.get_num_of_ranges = iwl_dump_ini_mem_ranges, +		.get_size = iwl_dump_ini_mem_get_size, +		.fill_mem_hdr = iwl_dump_ini_mem_fill_header, +		.fill_range = iwl_dump_ini_prph_iter, +	}, +	[IWL_FW_INI_REGION_PERIPHERY_PHY] = {}, +	[IWL_FW_INI_REGION_PERIPHERY_AUX] = {}, +	[IWL_FW_INI_REGION_DRAM_BUFFER] = { +		.get_num_of_ranges = iwl_dump_ini_mon_dram_ranges, +		.get_size = iwl_dump_ini_mon_dram_get_size, +		.fill_mem_hdr = iwl_dump_ini_mon_dram_fill_header, +		.fill_range = iwl_dump_ini_mon_dram_iter, +	}, +	[IWL_FW_INI_REGION_DRAM_IMR] = {}, +	[IWL_FW_INI_REGION_INTERNAL_BUFFER] = { +		.get_num_of_ranges = iwl_dump_ini_mem_ranges, +		.get_size = iwl_dump_ini_mon_smem_get_size, +		.fill_mem_hdr = iwl_dump_ini_mon_smem_fill_header, +		.fill_range = iwl_dump_ini_dev_mem_iter, +	}, +	[IWL_FW_INI_REGION_TXF] = { +		.get_num_of_ranges = iwl_dump_ini_txf_ranges, +		.get_size = iwl_dump_ini_txf_get_size, +		.fill_mem_hdr = iwl_dump_ini_mem_fill_header, +		.fill_range = iwl_dump_ini_txf_iter, +	}, +	[IWL_FW_INI_REGION_RXF] = { +		.get_num_of_ranges = iwl_dump_ini_rxf_ranges, +		.get_size = iwl_dump_ini_rxf_get_size, +		.fill_mem_hdr = iwl_dump_ini_mem_fill_header, +		.fill_range = iwl_dump_ini_rxf_iter, +	}, +	[IWL_FW_INI_REGION_PAGING] = { +		.fill_mem_hdr = iwl_dump_ini_mem_fill_header, +		.get_num_of_ranges = iwl_dump_ini_paging_ranges, +		.get_size = iwl_dump_ini_paging_get_size, +		.fill_range = iwl_dump_ini_paging_iter, +	}, +	[IWL_FW_INI_REGION_CSR] = { +		.get_num_of_ranges = iwl_dump_ini_mem_ranges, +		.get_size = iwl_dump_ini_mem_get_size, +		.fill_mem_hdr = iwl_dump_ini_mem_fill_header, +		.fill_range = iwl_dump_ini_csr_iter, +	}, +	[IWL_FW_INI_REGION_NOTIFICATION] = {}, +	[IWL_FW_INI_REGION_DHC] = {}, +	[IWL_FW_INI_REGION_LMAC_ERROR_TABLE] = { +		.get_num_of_ranges = iwl_dump_ini_mem_ranges, +		.get_size = iwl_dump_ini_mem_get_size, +		.fill_mem_hdr = iwl_dump_ini_mem_fill_header, +		.fill_range = iwl_dump_ini_dev_mem_iter, +	}, +	[IWL_FW_INI_REGION_UMAC_ERROR_TABLE] = { +		.get_num_of_ranges = iwl_dump_ini_mem_ranges, +		.get_size = iwl_dump_ini_mem_get_size, +		.fill_mem_hdr = iwl_dump_ini_mem_fill_header, +		.fill_range = iwl_dump_ini_dev_mem_iter, +	}, +}; -	if (!trigger || !trigger->num_regions) -		return 0; +static u32 iwl_dump_ini_trigger(struct iwl_fw_runtime *fwrt, +				struct iwl_fw_ini_trigger *trigger, +				struct list_head *list) +{ +	int i; +	u32 size = 0;  	for (i = 0; i < le32_to_cpu(trigger->num_regions); i++) { -		u32 reg_id = le32_to_cpu(trigger->data[i]); +		u32 reg_id = le32_to_cpu(trigger->data[i]), reg_type;  		struct iwl_fw_ini_region_cfg *reg;  		if (WARN_ON(reg_id >= ARRAY_SIZE(fwrt->dump.active_regs))) @@ -1802,7 +1877,7 @@ static int iwl_fw_ini_get_trigger_len(struct iwl_fw_runtime *fwrt,  		reg = fwrt->dump.active_regs[reg_id];  		if (!reg) {  			IWL_WARN(fwrt, -				 "WRT: unassigned region id %d, skipping\n", +				 "WRT: Unassigned region id %d, skipping\n",  				 reg_id);  			continue;  		} @@ -1811,205 +1886,55 @@ static int iwl_fw_ini_get_trigger_len(struct iwl_fw_runtime *fwrt,  		if (le32_to_cpu(reg->domain) != IWL_FW_INI_DBG_DOMAIN_ALWAYS_ON)  			continue; -		switch (le32_to_cpu(reg->region_type)) { -		case IWL_FW_INI_REGION_DEVICE_MEMORY: -		case IWL_FW_INI_REGION_PERIPHERY_MAC: -		case IWL_FW_INI_REGION_PERIPHERY_PHY: -		case IWL_FW_INI_REGION_PERIPHERY_AUX: -		case IWL_FW_INI_REGION_CSR: -		case IWL_FW_INI_REGION_LMAC_ERROR_TABLE: -		case IWL_FW_INI_REGION_UMAC_ERROR_TABLE: -			size = iwl_dump_ini_mem_get_size(fwrt, reg); -			if (size) -				ret_size += hdr_len + size; -			break; -		case IWL_FW_INI_REGION_TXF: -			size = iwl_dump_ini_txf_get_size(fwrt, reg); -			if (size) -				ret_size += hdr_len + size; -			break; -		case IWL_FW_INI_REGION_RXF: -			size = iwl_dump_ini_rxf_get_size(fwrt, reg); -			if (size) -				ret_size += hdr_len + size; -			break; -		case IWL_FW_INI_REGION_PAGING: -			if (iwl_fw_dbg_is_paging_enabled(fwrt)) -				size = iwl_dump_ini_paging_get_size(fwrt, reg); -			else -				size = iwl_dump_ini_paging_gen2_get_size(fwrt, -									 reg); -			if (size) -				ret_size += hdr_len + size; -			break; -		case IWL_FW_INI_REGION_DRAM_BUFFER: -			if (!fwrt->trans->dbg.num_blocks) -				break; -			size = iwl_dump_ini_mon_dram_get_size(fwrt, reg); -			if (size) -				ret_size += hdr_len + size; -			break; -		case IWL_FW_INI_REGION_INTERNAL_BUFFER: -			size = iwl_dump_ini_mon_smem_get_size(fwrt, reg); -			if (size) -				ret_size += hdr_len + size; -			break; -		case IWL_FW_INI_REGION_DRAM_IMR: -			/* Undefined yet */ -		default: -			break; -		} -	} - -	/* add dump info size */ -	if (ret_size) -		ret_size += hdr_len + sizeof(struct iwl_fw_ini_dump_info) + -			(le32_to_cpu(trigger->num_regions) * sizeof(__le32)); - -	return ret_size; -} - -static void iwl_fw_ini_dump_trigger(struct iwl_fw_runtime *fwrt, -				    struct iwl_fw_ini_trigger *trigger, -				    struct iwl_fw_error_dump_data **data) -{ -	int i, num = le32_to_cpu(trigger->num_regions); - -	iwl_dump_ini_info(fwrt, trigger, data); - -	for (i = 0; i < num; i++) { -		u32 reg_id = le32_to_cpu(trigger->data[i]); -		struct iwl_fw_ini_region_cfg *reg; -		struct iwl_dump_ini_mem_ops ops; - -		if (reg_id >= ARRAY_SIZE(fwrt->dump.active_regs)) +		reg_type = le32_to_cpu(reg->region_type); +		if (reg_type >= ARRAY_SIZE(iwl_dump_ini_region_ops))  			continue; -		reg = fwrt->dump.active_regs[reg_id]; -		/* Don't warn, get_trigger_len already warned */ -		if (!reg) -			continue; - -		/* currently the driver supports always on domain only */ -		if (le32_to_cpu(reg->domain) != IWL_FW_INI_DBG_DOMAIN_ALWAYS_ON) -			continue; +		size += iwl_dump_ini_mem(fwrt, list, reg, +					 &iwl_dump_ini_region_ops[reg_type]); +	} -		switch (le32_to_cpu(reg->region_type)) { -		case IWL_FW_INI_REGION_DEVICE_MEMORY: -		case IWL_FW_INI_REGION_LMAC_ERROR_TABLE: -		case IWL_FW_INI_REGION_UMAC_ERROR_TABLE: -			ops.get_num_of_ranges = iwl_dump_ini_mem_ranges; -			ops.get_size = iwl_dump_ini_mem_get_size; -			ops.fill_mem_hdr = iwl_dump_ini_mem_fill_header; -			ops.fill_range = iwl_dump_ini_dev_mem_iter; -			iwl_dump_ini_mem(fwrt, data, reg, &ops); -			break; -		case IWL_FW_INI_REGION_PERIPHERY_MAC: -		case IWL_FW_INI_REGION_PERIPHERY_PHY: -		case IWL_FW_INI_REGION_PERIPHERY_AUX: -			ops.get_num_of_ranges =	iwl_dump_ini_mem_ranges; -			ops.get_size = iwl_dump_ini_mem_get_size; -			ops.fill_mem_hdr = iwl_dump_ini_mem_fill_header; -			ops.fill_range = iwl_dump_ini_prph_iter; -			iwl_dump_ini_mem(fwrt, data, reg, &ops); -			break; -		case IWL_FW_INI_REGION_DRAM_BUFFER: -			ops.get_num_of_ranges = iwl_dump_ini_mon_dram_ranges; -			ops.get_size = iwl_dump_ini_mon_dram_get_size; -			ops.fill_mem_hdr = iwl_dump_ini_mon_dram_fill_header; -			ops.fill_range = iwl_dump_ini_mon_dram_iter; -			iwl_dump_ini_mem(fwrt, data, reg, &ops); -			break; -		case IWL_FW_INI_REGION_INTERNAL_BUFFER: -			ops.get_num_of_ranges = iwl_dump_ini_mem_ranges; -			ops.get_size = iwl_dump_ini_mon_smem_get_size; -			ops.fill_mem_hdr = iwl_dump_ini_mon_smem_fill_header; -			ops.fill_range = iwl_dump_ini_dev_mem_iter; -			iwl_dump_ini_mem(fwrt, data, reg, &ops); -			break; -		case IWL_FW_INI_REGION_PAGING: -			ops.fill_mem_hdr = iwl_dump_ini_mem_fill_header; -			if (iwl_fw_dbg_is_paging_enabled(fwrt)) { -				ops.get_num_of_ranges = -					iwl_dump_ini_paging_ranges; -				ops.get_size = iwl_dump_ini_paging_get_size; -				ops.fill_range = iwl_dump_ini_paging_iter; -			} else { -				ops.get_num_of_ranges = -					iwl_dump_ini_paging_gen2_ranges; -				ops.get_size = -					iwl_dump_ini_paging_gen2_get_size; -				ops.fill_range = iwl_dump_ini_paging_gen2_iter; -			} +	if (size) +		size += iwl_dump_ini_info(fwrt, trigger, list); -			iwl_dump_ini_mem(fwrt, data, reg, &ops); -			break; -		case IWL_FW_INI_REGION_TXF: { -			struct iwl_ini_txf_iter_data iter = { .init = true }; -			void *fifo_iter = fwrt->dump.fifo_iter; - -			fwrt->dump.fifo_iter = &iter; -			ops.get_num_of_ranges = iwl_dump_ini_txf_ranges; -			ops.get_size = iwl_dump_ini_txf_get_size; -			ops.fill_mem_hdr = iwl_dump_ini_mem_fill_header; -			ops.fill_range = iwl_dump_ini_txf_iter; -			iwl_dump_ini_mem(fwrt, data, reg, &ops); -			fwrt->dump.fifo_iter = fifo_iter; -			break; -		} -		case IWL_FW_INI_REGION_RXF: -			ops.get_num_of_ranges = iwl_dump_ini_rxf_ranges; -			ops.get_size = iwl_dump_ini_rxf_get_size; -			ops.fill_mem_hdr = iwl_dump_ini_mem_fill_header; -			ops.fill_range = iwl_dump_ini_rxf_iter; -			iwl_dump_ini_mem(fwrt, data, reg, &ops); -			break; -		case IWL_FW_INI_REGION_CSR: -			ops.get_num_of_ranges =	iwl_dump_ini_mem_ranges; -			ops.get_size = iwl_dump_ini_mem_get_size; -			ops.fill_mem_hdr = iwl_dump_ini_mem_fill_header; -			ops.fill_range = iwl_dump_ini_csr_iter; -			iwl_dump_ini_mem(fwrt, data, reg, &ops); -			break; -		case IWL_FW_INI_REGION_DRAM_IMR: -			/* This is undefined yet */ -		default: -			break; -		} -	} +	return size;  } -static struct iwl_fw_error_dump_file * -iwl_fw_error_ini_dump_file(struct iwl_fw_runtime *fwrt, -			   enum iwl_fw_ini_trigger_id trig_id) +static u32 iwl_dump_ini_file_gen(struct iwl_fw_runtime *fwrt, +				 enum iwl_fw_ini_trigger_id trig_id, +				 struct list_head *list)  { -	int size; -	struct iwl_fw_error_dump_data *dump_data; -	struct iwl_fw_error_dump_file *dump_file; +	struct iwl_fw_ini_dump_entry *entry; +	struct iwl_fw_ini_dump_file_hdr *hdr;  	struct iwl_fw_ini_trigger *trigger; +	u32 size;  	if (!iwl_fw_ini_trigger_on(fwrt, trig_id)) -		return NULL; +		return 0;  	trigger = fwrt->dump.active_trigs[trig_id].trig; +	if (!trigger || !le32_to_cpu(trigger->num_regions)) +		return 0; -	size = iwl_fw_ini_get_trigger_len(fwrt, trigger); -	if (!size) -		return NULL; +	entry = kmalloc(sizeof(*entry) + sizeof(*hdr), GFP_KERNEL); +	if (!entry) +		return 0; -	size += sizeof(*dump_file); +	entry->size = sizeof(*hdr); -	dump_file = vzalloc(size); -	if (!dump_file) -		return NULL; +	size = iwl_dump_ini_trigger(fwrt, trigger, list); +	if (!size) { +		kfree(entry); +		return 0; +	} -	dump_file->barker = cpu_to_le32(IWL_FW_INI_ERROR_DUMP_BARKER); -	dump_data = (void *)dump_file->data; -	dump_file->file_len = cpu_to_le32(size); +	hdr = (void *)entry->data; +	hdr->barker = cpu_to_le32(IWL_FW_INI_ERROR_DUMP_BARKER); +	hdr->file_len = cpu_to_le32(size + entry->size); -	iwl_fw_ini_dump_trigger(fwrt, trigger, &dump_data); +	list_add(&entry->list, list); -	return dump_file; +	return le32_to_cpu(hdr->file_len);  }  static void iwl_fw_error_dump(struct iwl_fw_runtime *fwrt) @@ -2058,27 +1983,44 @@ out:  	iwl_fw_free_dump_desc(fwrt);  } +static void iwl_dump_ini_list_free(struct list_head *list) +{ +	while (!list_empty(list)) { +		struct iwl_fw_ini_dump_entry *entry = +			list_entry(list->next, typeof(*entry), list); + +		list_del(&entry->list); +		kfree(entry); +	} +} +  static void iwl_fw_error_ini_dump(struct iwl_fw_runtime *fwrt, u8 wk_idx)  {  	enum iwl_fw_ini_trigger_id trig_id = fwrt->dump.wks[wk_idx].ini_trig_id; -	struct iwl_fw_error_dump_file *dump_file; +	struct list_head dump_list = LIST_HEAD_INIT(dump_list);  	struct scatterlist *sg_dump_data;  	u32 file_len; -	dump_file = iwl_fw_error_ini_dump_file(fwrt, trig_id); -	if (!dump_file) +	file_len = iwl_dump_ini_file_gen(fwrt, trig_id, &dump_list); +	if (!file_len)  		goto out; -	file_len = le32_to_cpu(dump_file->file_len); -  	sg_dump_data = alloc_sgtable(file_len);  	if (sg_dump_data) { -		sg_pcopy_from_buffer(sg_dump_data, sg_nents(sg_dump_data), -				     dump_file, file_len, 0); +		struct iwl_fw_ini_dump_entry *entry; +		int sg_entries = sg_nents(sg_dump_data); +		u32 offs = 0; + +		list_for_each_entry(entry, &dump_list, list) { +			sg_pcopy_from_buffer(sg_dump_data, sg_entries, +					     entry->data, entry->size, offs); +			offs += entry->size; +		}  		dev_coredumpsg(fwrt->trans->dev, sg_dump_data, file_len,  			       GFP_KERNEL);  	} -	vfree(dump_file); +	iwl_dump_ini_list_free(&dump_list); +  out:  	fwrt->dump.wks[wk_idx].ini_trig_id = IWL_FW_TRIGGER_ID_INVALID;  } @@ -2098,7 +2040,7 @@ int iwl_fw_dbg_collect_desc(struct iwl_fw_runtime *fwrt,  	u32 trig_type = le32_to_cpu(desc->trig_desc.type);  	int ret; -	if (fwrt->trans->dbg.ini_valid) { +	if (iwl_trans_dbg_ini_valid(fwrt->trans)) {  		ret = iwl_fw_dbg_ini_collect(fwrt, trig_type);  		if (!ret)  			iwl_fw_free_dump_desc(fwrt); @@ -2220,7 +2162,7 @@ int _iwl_fw_dbg_ini_collect(struct iwl_fw_runtime *fwrt,  	active->trig->occurrences = cpu_to_le32(--occur);  	if (le32_to_cpu(active->trig->force_restart)) { -		IWL_WARN(fwrt, "WRT: force restart: trigger %d fired.\n", id); +		IWL_WARN(fwrt, "WRT: Force restart: trigger %d fired.\n", id);  		iwl_force_nmi(fwrt->trans);  		return 0;  	} @@ -2240,7 +2182,7 @@ int _iwl_fw_dbg_ini_collect(struct iwl_fw_runtime *fwrt,  	fwrt->dump.wks[idx].ini_trig_id = id; -	IWL_WARN(fwrt, "WRT: collecting data: ini trigger %d fired.\n", id); +	IWL_WARN(fwrt, "WRT: Collecting data: ini trigger %d fired.\n", id);  	schedule_delayed_work(&fwrt->dump.wks[idx].wk, usecs_to_jiffies(delay)); @@ -2372,16 +2314,19 @@ static void iwl_fw_dbg_collect_sync(struct iwl_fw_runtime *fwrt, u8 wk_idx)  		goto out;  	} -	iwl_fw_dbg_stop_recording(fwrt->trans, ¶ms); +	if (iwl_fw_dbg_stop_restart_recording(fwrt, ¶ms, true)) { +		IWL_ERR(fwrt, "Failed to stop DBGC recording, aborting dump\n"); +		goto out; +	} -	IWL_DEBUG_FW_INFO(fwrt, "WRT: data collection start\n"); -	if (fwrt->trans->dbg.ini_valid) +	IWL_DEBUG_FW_INFO(fwrt, "WRT: Data collection start\n"); +	if (iwl_trans_dbg_ini_valid(fwrt->trans))  		iwl_fw_error_ini_dump(fwrt, wk_idx);  	else  		iwl_fw_error_dump(fwrt); -	IWL_DEBUG_FW_INFO(fwrt, "WRT: data collection done\n"); +	IWL_DEBUG_FW_INFO(fwrt, "WRT: Data collection done\n"); -	iwl_fw_dbg_restart_recording(fwrt, ¶ms); +	iwl_fw_dbg_stop_restart_recording(fwrt, ¶ms, false);  out:  	clear_bit(wk_idx, &fwrt->dump.active_wks); @@ -2432,472 +2377,17 @@ void iwl_fw_dbg_read_d3_debug_data(struct iwl_fw_runtime *fwrt)  }  IWL_EXPORT_SYMBOL(iwl_fw_dbg_read_d3_debug_data); -static void iwl_fw_dbg_info_apply(struct iwl_fw_runtime *fwrt, -				  struct iwl_fw_ini_debug_info_tlv *dbg_info, -				  bool ext, enum iwl_fw_ini_apply_point pnt) -{ -	u32 img_name_len = le32_to_cpu(dbg_info->img_name_len); -	u32 dbg_cfg_name_len = le32_to_cpu(dbg_info->dbg_cfg_name_len); - -	if (img_name_len != IWL_FW_INI_MAX_IMG_NAME_LEN) { -		IWL_WARN(fwrt, -			 "WRT: ext=%d. Invalid image name length %d, expected %d\n", -			 ext, img_name_len, -			 IWL_FW_INI_MAX_IMG_NAME_LEN); -		return; -	} - -	if (dbg_cfg_name_len != IWL_FW_INI_MAX_DBG_CFG_NAME_LEN) { -		IWL_WARN(fwrt, -			 "WRT: ext=%d. Invalid debug cfg name length %d, expected %d\n", -			 ext, dbg_cfg_name_len, -			 IWL_FW_INI_MAX_DBG_CFG_NAME_LEN); -		return; -	} - -	if (ext) { -		memcpy(fwrt->dump.external_dbg_cfg_name, dbg_info->dbg_cfg_name, -		       sizeof(fwrt->dump.external_dbg_cfg_name)); -	} else { -		memcpy(fwrt->dump.img_name, dbg_info->img_name, -		       sizeof(fwrt->dump.img_name)); -		memcpy(fwrt->dump.internal_dbg_cfg_name, dbg_info->dbg_cfg_name, -		       sizeof(fwrt->dump.internal_dbg_cfg_name)); -	} -} - -static void -iwl_fw_dbg_buffer_allocation(struct iwl_fw_runtime *fwrt, u32 size) -{ -	struct iwl_trans *trans = fwrt->trans; -	void *virtual_addr = NULL; -	dma_addr_t phys_addr; - -	if (WARN_ON_ONCE(trans->dbg.num_blocks == -			 ARRAY_SIZE(trans->dbg.fw_mon))) -		return; - -	virtual_addr = -		dma_alloc_coherent(fwrt->trans->dev, size, &phys_addr, -				   GFP_KERNEL | __GFP_NOWARN | __GFP_ZERO | -				   __GFP_COMP); - -	/* TODO: alloc fragments if needed */ -	if (!virtual_addr) -		IWL_ERR(fwrt, "Failed to allocate debug memory\n"); - -	IWL_DEBUG_FW(trans, -		     "Allocated DRAM buffer[%d], size=0x%x\n", -		     trans->dbg.num_blocks, size); - -	trans->dbg.fw_mon[trans->dbg.num_blocks].block = virtual_addr; -	trans->dbg.fw_mon[trans->dbg.num_blocks].physical = phys_addr; -	trans->dbg.fw_mon[trans->dbg.num_blocks].size = size; -	trans->dbg.num_blocks++; -} - -static void iwl_fw_dbg_buffer_apply(struct iwl_fw_runtime *fwrt, -				    struct iwl_fw_ini_allocation_data *alloc, -				    enum iwl_fw_ini_apply_point pnt) -{ -	struct iwl_trans *trans = fwrt->trans; -	struct iwl_ldbg_config_cmd ldbg_cmd = { -		.type = cpu_to_le32(BUFFER_ALLOCATION), -	}; -	struct iwl_buffer_allocation_cmd *cmd = &ldbg_cmd.buffer_allocation; -	struct iwl_host_cmd hcmd = { -		.id = LDBG_CONFIG_CMD, -		.flags = CMD_ASYNC, -		.data[0] = &ldbg_cmd, -		.len[0] = sizeof(ldbg_cmd), -	}; -	int block_idx = trans->dbg.num_blocks; -	u32 buf_location = le32_to_cpu(alloc->tlv.buffer_location); - -	if (fwrt->trans->dbg.ini_dest == IWL_FW_INI_LOCATION_INVALID) -		fwrt->trans->dbg.ini_dest = buf_location; - -	if (buf_location != fwrt->trans->dbg.ini_dest) { -		WARN(fwrt, -		     "WRT: attempt to override buffer location on apply point %d\n", -		     pnt); - -		return; -	} - -	if (buf_location == IWL_FW_INI_LOCATION_SRAM_PATH) { -		IWL_DEBUG_FW(trans, "WRT: applying SMEM buffer destination\n"); -		/* set sram monitor by enabling bit 7 */ -		iwl_set_bit(fwrt->trans, CSR_HW_IF_CONFIG_REG, -			    CSR_HW_IF_CONFIG_REG_BIT_MONITOR_SRAM); - -		return; -	} - -	if (buf_location != IWL_FW_INI_LOCATION_DRAM_PATH) -		return; - -	if (!alloc->is_alloc) { -		iwl_fw_dbg_buffer_allocation(fwrt, -					     le32_to_cpu(alloc->tlv.size)); -		if (block_idx == trans->dbg.num_blocks) -			return; -		alloc->is_alloc = 1; -	} - -	/* First block is assigned via registers / context info */ -	if (trans->dbg.num_blocks == 1) -		return; - -	IWL_DEBUG_FW(trans, -		     "WRT: applying DRAM buffer[%d] destination\n", block_idx); - -	cmd->num_frags = cpu_to_le32(1); -	cmd->fragments[0].address = -		cpu_to_le64(trans->dbg.fw_mon[block_idx].physical); -	cmd->fragments[0].size = alloc->tlv.size; -	cmd->allocation_id = alloc->tlv.allocation_id; -	cmd->buffer_location = alloc->tlv.buffer_location; - -	iwl_trans_send_cmd(trans, &hcmd); -} - -static void iwl_fw_dbg_send_hcmd(struct iwl_fw_runtime *fwrt, -				 struct iwl_ucode_tlv *tlv, -				 bool ext) -{ -	struct iwl_fw_ini_hcmd_tlv *hcmd_tlv = (void *)&tlv->data[0]; -	struct iwl_fw_ini_hcmd *data = &hcmd_tlv->hcmd; -	u16 len = le32_to_cpu(tlv->length) - sizeof(*hcmd_tlv); - -	struct iwl_host_cmd hcmd = { -		.id = WIDE_ID(data->group, data->id), -		.len = { len, }, -		.data = { data->data, }, -	}; - -	/* currently the driver supports always on domain only */ -	if (le32_to_cpu(hcmd_tlv->domain) != IWL_FW_INI_DBG_DOMAIN_ALWAYS_ON) -		return; - -	IWL_DEBUG_FW(fwrt, -		     "WRT: ext=%d. Sending host command id=0x%x, group=0x%x\n", -		     ext, data->id, data->group); - -	iwl_trans_send_cmd(fwrt->trans, &hcmd); -} - -static void iwl_fw_dbg_update_regions(struct iwl_fw_runtime *fwrt, -				      struct iwl_fw_ini_region_tlv *tlv, -				      bool ext, enum iwl_fw_ini_apply_point pnt) -{ -	void *iter = (void *)tlv->region_config; -	int i, size = le32_to_cpu(tlv->num_regions); -	const char *err_st = -		"WRT: ext=%d. Invalid region %s %d for apply point %d\n"; - -	for (i = 0; i < size; i++) { -		struct iwl_fw_ini_region_cfg *reg = iter, **active; -		int id = le32_to_cpu(reg->region_id); -		u32 type = le32_to_cpu(reg->region_type); - -		if (WARN(id >= ARRAY_SIZE(fwrt->dump.active_regs), err_st, ext, -			 "id", id, pnt)) -			break; - -		if (WARN(type == 0 || type >= IWL_FW_INI_REGION_NUM, err_st, -			 ext, "type", type, pnt)) -			break; - -		active = &fwrt->dump.active_regs[id]; - -		if (*active) -			IWL_WARN(fwrt->trans, -				 "WRT: ext=%d. Region id %d override\n", -				 ext, id); - -		IWL_DEBUG_FW(fwrt, -			     "WRT: ext=%d. Activating region id %d\n", -			     ext, id); - -		*active = reg; - -		if (type == IWL_FW_INI_REGION_TXF || -		    type == IWL_FW_INI_REGION_RXF) -			iter += le32_to_cpu(reg->fifos.num_of_registers) * -				sizeof(__le32); -		else if (type == IWL_FW_INI_REGION_DEVICE_MEMORY || -			 type == IWL_FW_INI_REGION_PERIPHERY_MAC || -			 type == IWL_FW_INI_REGION_PERIPHERY_PHY || -			 type == IWL_FW_INI_REGION_PERIPHERY_AUX || -			 type == IWL_FW_INI_REGION_INTERNAL_BUFFER || -			 type == IWL_FW_INI_REGION_PAGING || -			 type == IWL_FW_INI_REGION_CSR || -			 type == IWL_FW_INI_REGION_LMAC_ERROR_TABLE || -			 type == IWL_FW_INI_REGION_UMAC_ERROR_TABLE) -			iter += le32_to_cpu(reg->internal.num_of_ranges) * -				sizeof(__le32); - -		iter += sizeof(*reg); -	} -} - -static int iwl_fw_dbg_trig_realloc(struct iwl_fw_runtime *fwrt, -				   struct iwl_fw_ini_active_triggers *active, -				   u32 id, int size) -{ -	void *ptr; - -	if (size <= active->size) -		return 0; - -	ptr = krealloc(active->trig, size, GFP_KERNEL); -	if (!ptr) { -		IWL_ERR(fwrt, "WRT: Failed to allocate memory for trigger %d\n", -			id); -		return -ENOMEM; -	} -	active->trig = ptr; -	active->size = size; - -	return 0; -} - -static void iwl_fw_dbg_update_triggers(struct iwl_fw_runtime *fwrt, -				       struct iwl_fw_ini_trigger_tlv *tlv, -				       bool ext, -				       enum iwl_fw_ini_apply_point apply_point) -{ -	int i, size = le32_to_cpu(tlv->num_triggers); -	void *iter = (void *)tlv->trigger_config; - -	for (i = 0; i < size; i++) { -		struct iwl_fw_ini_trigger *trig = iter; -		struct iwl_fw_ini_active_triggers *active; -		int id = le32_to_cpu(trig->trigger_id); -		u32 trig_regs_size = le32_to_cpu(trig->num_regions) * -			sizeof(__le32); - -		if (WARN(id >= ARRAY_SIZE(fwrt->dump.active_trigs), -			 "WRT: ext=%d. Invalid trigger id %d for apply point %d\n", -			 ext, id, apply_point)) -			break; - -		active = &fwrt->dump.active_trigs[id]; - -		if (!active->active) { -			size_t trig_size = sizeof(*trig) + trig_regs_size; - -			IWL_DEBUG_FW(fwrt, -				     "WRT: ext=%d. Activating trigger %d\n", -				     ext, id); - -			if (iwl_fw_dbg_trig_realloc(fwrt, active, id, -						    trig_size)) -				goto next; - -			memcpy(active->trig, trig, trig_size); - -		} else { -			u32 conf_override = -				!(le32_to_cpu(trig->override_trig) & 0xff); -			u32 region_override = -				!(le32_to_cpu(trig->override_trig) & 0xff00); -			u32 offset = 0; -			u32 active_regs = -				le32_to_cpu(active->trig->num_regions); -			u32 new_regs = le32_to_cpu(trig->num_regions); -			int mem_to_add = trig_regs_size; - -			if (region_override) { -				IWL_DEBUG_FW(fwrt, -					     "WRT: ext=%d. Trigger %d regions override\n", -					     ext, id); - -				mem_to_add -= active_regs * sizeof(__le32); -			} else { -				IWL_DEBUG_FW(fwrt, -					     "WRT: ext=%d. Trigger %d regions appending\n", -					     ext, id); - -				offset += active_regs; -				new_regs += active_regs; -			} - -			if (iwl_fw_dbg_trig_realloc(fwrt, active, id, -						    active->size + mem_to_add)) -				goto next; - -			if (conf_override) { -				IWL_DEBUG_FW(fwrt, -					     "WRT: ext=%d. Trigger %d configuration override\n", -					     ext, id); - -				memcpy(active->trig, trig, sizeof(*trig)); -			} - -			memcpy(active->trig->data + offset, trig->data, -			       trig_regs_size); -			active->trig->num_regions = cpu_to_le32(new_regs); -		} - -		/* Since zero means infinity - just set to -1 */ -		if (!le32_to_cpu(active->trig->occurrences)) -			active->trig->occurrences = cpu_to_le32(-1); - -		active->active = true; - -		if (id == IWL_FW_TRIGGER_ID_PERIODIC_TRIGGER) { -			u32 collect_interval = le32_to_cpu(trig->trigger_data); - -			/* the minimum allowed interval is 50ms */ -			if (collect_interval < 50) { -				collect_interval = 50; -				trig->trigger_data = -					cpu_to_le32(collect_interval); -			} - -			mod_timer(&fwrt->dump.periodic_trig, -				  jiffies + msecs_to_jiffies(collect_interval)); -		} -next: -		iter += sizeof(*trig) + trig_regs_size; - -	} -} - -static void _iwl_fw_dbg_apply_point(struct iwl_fw_runtime *fwrt, -				    struct iwl_apply_point_data *data, -				    enum iwl_fw_ini_apply_point pnt, -				    bool ext) -{ -	void *iter = data->data; - -	while (iter && iter < data->data + data->size) { -		struct iwl_ucode_tlv *tlv = iter; -		void *ini_tlv = (void *)tlv->data; -		u32 type = le32_to_cpu(tlv->type); - -		switch (type) { -		case IWL_UCODE_TLV_TYPE_DEBUG_INFO: -			iwl_fw_dbg_info_apply(fwrt, ini_tlv, ext, pnt); -			break; -		case IWL_UCODE_TLV_TYPE_BUFFER_ALLOCATION: { -			struct iwl_fw_ini_allocation_data *buf_alloc = ini_tlv; - -			if (pnt != IWL_FW_INI_APPLY_EARLY) { -				IWL_ERR(fwrt, -					"WRT: ext=%d. Invalid apply point %d for buffer allocation\n", -					ext, pnt); -				goto next; -			} - -			iwl_fw_dbg_buffer_apply(fwrt, ini_tlv, pnt); -			iter += sizeof(buf_alloc->is_alloc); -			break; -		} -		case IWL_UCODE_TLV_TYPE_HCMD: -			if (pnt < IWL_FW_INI_APPLY_AFTER_ALIVE) { -				IWL_ERR(fwrt, -					"WRT: ext=%d. Invalid apply point %d for host command\n", -					ext, pnt); -				goto next; -			} -			iwl_fw_dbg_send_hcmd(fwrt, tlv, ext); -			break; -		case IWL_UCODE_TLV_TYPE_REGIONS: -			iwl_fw_dbg_update_regions(fwrt, ini_tlv, ext, pnt); -			break; -		case IWL_UCODE_TLV_TYPE_TRIGGERS: -			iwl_fw_dbg_update_triggers(fwrt, ini_tlv, ext, pnt); -			break; -		case IWL_UCODE_TLV_TYPE_DEBUG_FLOW: -			break; -		default: -			WARN_ONCE(1, -				  "WRT: ext=%d. Invalid TLV 0x%x for apply point\n", -				  ext, type); -			break; -		} -next: -		iter += sizeof(*tlv) + le32_to_cpu(tlv->length); -	} -} - -static void iwl_fw_dbg_ini_reset_cfg(struct iwl_fw_runtime *fwrt) +void iwl_fw_dbg_stop_sync(struct iwl_fw_runtime *fwrt)  {  	int i; -	for (i = 0; i < IWL_FW_INI_MAX_REGION_ID; i++) -		fwrt->dump.active_regs[i] = NULL; - -	/* disable the triggers, used in recovery flow */ -	for (i = 0; i < IWL_FW_TRIGGER_ID_NUM; i++) -		fwrt->dump.active_trigs[i].active = false; - -	memset(fwrt->dump.img_name, 0, -	       sizeof(fwrt->dump.img_name)); -	memset(fwrt->dump.internal_dbg_cfg_name, 0, -	       sizeof(fwrt->dump.internal_dbg_cfg_name)); -	memset(fwrt->dump.external_dbg_cfg_name, 0, -	       sizeof(fwrt->dump.external_dbg_cfg_name)); - -	fwrt->trans->dbg.ini_dest = IWL_FW_INI_LOCATION_INVALID; -} - -void iwl_fw_dbg_apply_point(struct iwl_fw_runtime *fwrt, -			    enum iwl_fw_ini_apply_point apply_point) -{ -	void *data = &fwrt->trans->dbg.apply_points[apply_point]; - -	IWL_DEBUG_FW(fwrt, "WRT: enabling apply point %d\n", apply_point); - -	if (apply_point == IWL_FW_INI_APPLY_EARLY) -		iwl_fw_dbg_ini_reset_cfg(fwrt); - -	_iwl_fw_dbg_apply_point(fwrt, data, apply_point, false); - -	data = &fwrt->trans->dbg.apply_points_ext[apply_point]; -	_iwl_fw_dbg_apply_point(fwrt, data, apply_point, true); -} -IWL_EXPORT_SYMBOL(iwl_fw_dbg_apply_point); - -void iwl_fwrt_stop_device(struct iwl_fw_runtime *fwrt) -{ -	int i; - -	del_timer(&fwrt->dump.periodic_trig); +	iwl_dbg_tlv_del_timers(fwrt->trans);  	for (i = 0; i < IWL_FW_RUNTIME_DUMP_WK_NUM; i++)  		iwl_fw_dbg_collect_sync(fwrt, i); -	iwl_trans_stop_device(fwrt->trans); -} -IWL_EXPORT_SYMBOL(iwl_fwrt_stop_device); - -void iwl_fw_dbg_periodic_trig_handler(struct timer_list *t) -{ -	struct iwl_fw_runtime *fwrt; -	enum iwl_fw_ini_trigger_id id = IWL_FW_TRIGGER_ID_PERIODIC_TRIGGER; -	int ret; -	typeof(fwrt->dump) *dump_ptr = container_of(t, typeof(fwrt->dump), -						    periodic_trig); - -	fwrt = container_of(dump_ptr, typeof(*fwrt), dump); - -	ret = _iwl_fw_dbg_ini_collect(fwrt, id); -	if (!ret || ret == -EBUSY) { -		struct iwl_fw_ini_trigger *trig = -			fwrt->dump.active_trigs[id].trig; -		u32 occur = le32_to_cpu(trig->occurrences); -		u32 collect_interval = le32_to_cpu(trig->trigger_data); - -		if (!occur) -			return; - -		mod_timer(&fwrt->dump.periodic_trig, -			  jiffies + msecs_to_jiffies(collect_interval)); -	} +	iwl_fw_dbg_stop_restart_recording(fwrt, NULL, true);  } +IWL_EXPORT_SYMBOL(iwl_fw_dbg_stop_sync);  #define FSEQ_REG(x) { .addr = (x), .str = #x, } @@ -2937,3 +2427,92 @@ void iwl_fw_error_print_fseq_regs(struct iwl_fw_runtime *fwrt)  	iwl_trans_release_nic_access(trans, &flags);  }  IWL_EXPORT_SYMBOL(iwl_fw_error_print_fseq_regs); + +static int iwl_fw_dbg_suspend_resume_hcmd(struct iwl_trans *trans, bool suspend) +{ +	struct iwl_dbg_suspend_resume_cmd cmd = { +		.operation = suspend ? +			cpu_to_le32(DBGC_SUSPEND_CMD) : +			cpu_to_le32(DBGC_RESUME_CMD), +	}; +	struct iwl_host_cmd hcmd = { +		.id = WIDE_ID(DEBUG_GROUP, DBGC_SUSPEND_RESUME), +		.data[0] = &cmd, +		.len[0] = sizeof(cmd), +	}; + +	return iwl_trans_send_cmd(trans, &hcmd); +} + +static void iwl_fw_dbg_stop_recording(struct iwl_trans *trans, +				      struct iwl_fw_dbg_params *params) +{ +	if (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_7000) { +		iwl_set_bits_prph(trans, MON_BUFF_SAMPLE_CTL, 0x100); +		return; +	} + +	if (params) { +		params->in_sample = iwl_read_umac_prph(trans, DBGC_IN_SAMPLE); +		params->out_ctrl = iwl_read_umac_prph(trans, DBGC_OUT_CTRL); +	} + +	iwl_write_umac_prph(trans, DBGC_IN_SAMPLE, 0); +	/* wait for the DBGC to finish writing the internal buffer to DRAM to +	 * avoid halting the HW while writing +	 */ +	usleep_range(700, 1000); +	iwl_write_umac_prph(trans, DBGC_OUT_CTRL, 0); +} + +static int iwl_fw_dbg_restart_recording(struct iwl_trans *trans, +					struct iwl_fw_dbg_params *params) +{ +	if (!params) +		return -EIO; + +	if (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_7000) { +		iwl_clear_bits_prph(trans, MON_BUFF_SAMPLE_CTL, 0x100); +		iwl_clear_bits_prph(trans, MON_BUFF_SAMPLE_CTL, 0x1); +		iwl_set_bits_prph(trans, MON_BUFF_SAMPLE_CTL, 0x1); +	} else { +		iwl_write_umac_prph(trans, DBGC_IN_SAMPLE, params->in_sample); +		iwl_write_umac_prph(trans, DBGC_OUT_CTRL, params->out_ctrl); +	} + +	return 0; +} + +int iwl_fw_dbg_stop_restart_recording(struct iwl_fw_runtime *fwrt, +				      struct iwl_fw_dbg_params *params, +				      bool stop) +{ +	int ret = 0; + +	/* if the FW crashed or not debug monitor cfg was given, there is +	 * no point in changing the recording state +	 */ +	if (test_bit(STATUS_FW_ERROR, &fwrt->trans->status) || +	    (!fwrt->trans->dbg.dest_tlv && +	     fwrt->trans->dbg.ini_dest == IWL_FW_INI_LOCATION_INVALID)) +		return 0; + +	if (fw_has_capa(&fwrt->fw->ucode_capa, +			IWL_UCODE_TLV_CAPA_DBG_SUSPEND_RESUME_CMD_SUPP)) +		ret = iwl_fw_dbg_suspend_resume_hcmd(fwrt->trans, stop); +	else if (stop) +		iwl_fw_dbg_stop_recording(fwrt->trans, params); +	else +		ret = iwl_fw_dbg_restart_recording(fwrt->trans, params); +#ifdef CONFIG_IWLWIFI_DEBUGFS +	if (!ret) { +		if (stop) +			fwrt->trans->dbg.rec_on = false; +		else +			iwl_fw_set_dbg_rec_on(fwrt); +	} +#endif + +	return ret; +} +IWL_EXPORT_SYMBOL(iwl_fw_dbg_stop_restart_recording);  |