Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5bdaa0ef authored by Johannes Berg's avatar Johannes Berg Committed by Luca Coelho
Browse files

iwlwifi: allow memory debug TLV to specify the memory type



Due to some new features and changes, the firmware file will now
specify what type of memory to dump, in upper 8 bits of the type
field of the TLV. Parse it (types we don't understand are errors)
and teach the code to dump periphery memory.

Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarLuca Coelho <luciano.coelho@intel.com>
parent 1110f8e3
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -1020,6 +1020,18 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
			IWL_DEBUG_INFO(drv, "Found debug memory segment: %u\n",
				       dbg_mem->data_type);

			switch (type & FW_DBG_MEM_TYPE_MASK) {
			case FW_DBG_MEM_TYPE_REGULAR:
			case FW_DBG_MEM_TYPE_PRPH:
				/* we know how to handle these */
				break;
			default:
				IWL_ERR(drv,
					"Found debug memory segment with invalid type: 0x%x\n",
					type);
				return -EINVAL;
			}

			size = sizeof(*pieces->dbg_mem_tlv) *
			       (pieces->n_dbg_mem_tlv + 1);
			n = krealloc(pieces->dbg_mem_tlv, size, GFP_KERNEL);
+14 −1
Original line number Diff line number Diff line
@@ -488,10 +488,23 @@ enum iwl_fw_dbg_monitor_mode {
	MIPI_MODE = 3,
};

/**
 * enum iwl_fw_mem_seg_type - memory segment type
 * @FW_DBG_MEM_TYPE_MASK: mask for the type indication
 * @FW_DBG_MEM_TYPE_REGULAR: regular memory
 * @FW_DBG_MEM_TYPE_PRPH: periphery memory (requires special reading)
 */
enum iwl_fw_mem_seg_type {
	FW_DBG_MEM_TYPE_MASK	= 0xff000000,
	FW_DBG_MEM_TYPE_REGULAR	= 0x00000000,
	FW_DBG_MEM_TYPE_PRPH	= 0x01000000,
};

/**
 * struct iwl_fw_dbg_mem_seg_tlv - configures the debug data memory segments
 *
 * @data_type: the memory segment type to record
 * @data_type: the memory segment type to record, see &enum iwl_fw_mem_seg_type
 *	for what we care about
 * @ofs: the memory segment offset
 * @len: the memory segment length, in bytes
 *
+53 −12
Original line number Diff line number Diff line
@@ -406,6 +406,30 @@ static const struct iwl_prph_range iwl_prph_dump_addr_9000[] = {
	{ .start = 0x00a02400, .end = 0x00a02758 },
};

static void _iwl_read_prph_block(struct iwl_trans *trans, u32 start,
				 u32 len_bytes, __le32 *data)
{
	u32 i;

	for (i = 0; i < len_bytes; i += 4)
		*data++ = cpu_to_le32(iwl_read_prph_no_grab(trans, start + i));
}

static bool iwl_read_prph_block(struct iwl_trans *trans, u32 start,
				u32 len_bytes, __le32 *data)
{
	unsigned long flags;
	bool success = false;

	if (iwl_trans_grab_nic_access(trans, &flags)) {
		success = true;
		_iwl_read_prph_block(trans, start, len_bytes, data);
		iwl_trans_release_nic_access(trans, &flags);
	}

	return success;
}

static void iwl_dump_prph(struct iwl_trans *trans,
			  struct iwl_fw_error_dump_data **data,
			  const struct iwl_prph_range *iwl_prph_dump_addr,
@@ -422,21 +446,18 @@ static void iwl_dump_prph(struct iwl_trans *trans,
		/* The range includes both boundaries */
		int num_bytes_in_chunk = iwl_prph_dump_addr[i].end -
			 iwl_prph_dump_addr[i].start + 4;
		int reg;
		__le32 *val;

		(*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PRPH);
		(*data)->len = cpu_to_le32(sizeof(*prph) +
					num_bytes_in_chunk);
		prph = (void *)(*data)->data;
		prph->prph_start = cpu_to_le32(iwl_prph_dump_addr[i].start);
		val = (void *)prph->data;

		for (reg = iwl_prph_dump_addr[i].start;
		     reg <= iwl_prph_dump_addr[i].end;
		     reg += 4)
			*val++ = cpu_to_le32(iwl_read_prph_no_grab(trans,
								   reg));
		_iwl_read_prph_block(trans, iwl_prph_dump_addr[i].start,
				     /* our range is inclusive, hence + 4 */
				     iwl_prph_dump_addr[i].end -
				     iwl_prph_dump_addr[i].start + 4,
				     (void *)prph->data);

		*data = iwl_fw_error_next_data(*data);
	}
@@ -716,15 +737,35 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
	for (i = 0; i < mvm->fw->n_dbg_mem_tlv; i++) {
		u32 len = le32_to_cpu(fw_dbg_mem[i].len);
		u32 ofs = le32_to_cpu(fw_dbg_mem[i].ofs);
		bool success;

		dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM);
		dump_data->len = cpu_to_le32(len + sizeof(*dump_mem));
		dump_mem = (void *)dump_data->data;
		dump_mem->type = fw_dbg_mem[i].data_type;
		dump_mem->offset = cpu_to_le32(ofs);

		switch (dump_mem->type & cpu_to_le32(FW_DBG_MEM_TYPE_MASK)) {
		case cpu_to_le32(FW_DBG_MEM_TYPE_REGULAR):
			iwl_trans_read_mem_bytes(mvm->trans, ofs,
						 dump_mem->data,
						 len);
			success = true;
			break;
		case cpu_to_le32(FW_DBG_MEM_TYPE_PRPH):
			success = iwl_read_prph_block(mvm->trans, ofs, len,
						      (void *)dump_mem->data);
			break;
		default:
			/*
			 * shouldn't get here, we ignored this kind
			 * of TLV earlier during the TLV parsing?!
			 */
			WARN_ON(1);
			success = false;
		}

		if (success)
			dump_data = iwl_fw_error_next_data(dump_data);
	}