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

Commit a1e88665 authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "icnss2: Add code for SSR dump collection feature for moselle"

parents 1fa69bcb c25c1b92
Loading
Loading
Loading
Loading
+102 −0
Original line number Diff line number Diff line
@@ -457,6 +457,50 @@ static struct qmi_elem_info wlfw_host_ddr_range_s_v01_ei[] = {
	},
};

static struct qmi_elem_info wlfw_m3_segment_info_s_v01_ei[] = {
	{
		.data_type      = QMI_SIGNED_4_BYTE_ENUM,
		.elem_len       = 1,
		.elem_size      = sizeof(enum wlfw_m3_segment_type_v01),
		.array_type       = NO_ARRAY,
		.tlv_type       = 0,
		.offset         = offsetof(struct wlfw_m3_segment_info_s_v01,
					   type),
	},
	{
		.data_type      = QMI_UNSIGNED_8_BYTE,
		.elem_len       = 1,
		.elem_size      = sizeof(u64),
		.array_type       = NO_ARRAY,
		.tlv_type       = 0,
		.offset         = offsetof(struct wlfw_m3_segment_info_s_v01,
					   addr),
	},
	{
		.data_type      = QMI_UNSIGNED_8_BYTE,
		.elem_len       = 1,
		.elem_size      = sizeof(u64),
		.array_type       = NO_ARRAY,
		.tlv_type       = 0,
		.offset         = offsetof(struct wlfw_m3_segment_info_s_v01,
					   size),
	},
	{
		.data_type      = QMI_STRING,
		.elem_len       = QMI_WLFW_MAX_STR_LEN_V01 + 1,
		.elem_size      = sizeof(char),
		.array_type       = NO_ARRAY,
		.tlv_type       = 0,
		.offset         = offsetof(struct wlfw_m3_segment_info_s_v01,
					   name),
	},
	{
		.data_type      = QMI_EOTI,
		.array_type       = NO_ARRAY,
		.tlv_type       = QMI_COMMON_TLV_TYPE,
	},
};

struct qmi_elem_info wlfw_ind_register_req_msg_v01_ei[] = {
	{
		.data_type      = QMI_OPT_FLAG,
@@ -800,6 +844,25 @@ struct qmi_elem_info wlfw_ind_register_req_msg_v01_ei[] = {
		.offset         = offsetof(struct wlfw_ind_register_req_msg_v01,
					   qdss_mem_ready_enable),
	},
	{
		.data_type      = QMI_OPT_FLAG,
		.elem_len       = 1,
		.elem_size      = sizeof(u8),
		.array_type       = NO_ARRAY,
		.tlv_type       = 0x23,
		.offset         =
		offsetof(struct wlfw_ind_register_req_msg_v01,
			 m3_dump_upload_segments_req_enable_valid),
	},
	{
		.data_type      = QMI_UNSIGNED_1_BYTE,
		.elem_len       = 1,
		.elem_size      = sizeof(u8),
		.array_type       = NO_ARRAY,
		.tlv_type       = 0x23,
		.offset         = offsetof(struct wlfw_ind_register_req_msg_v01,
					   m3_dump_upload_segments_req_enable),
	},
	{
		.data_type      = QMI_EOTI,
		.array_type       = NO_ARRAY,
@@ -4614,3 +4677,42 @@ struct qmi_elem_info wlfw_pcie_gen_switch_resp_msg_v01_ei[] = {
	},
};

struct qmi_elem_info wlfw_m3_dump_upload_segments_req_ind_msg_v01_ei[] = {
	{
		.data_type      = QMI_UNSIGNED_4_BYTE,
		.elem_len       = 1,
		.elem_size      = sizeof(u32),
		.array_type       = NO_ARRAY,
		.tlv_type       = 0x01,
		.offset         =
		offsetof(struct wlfw_m3_dump_upload_segments_req_ind_msg_v01,
			 pdev_id),
	},
	{
		.data_type      = QMI_UNSIGNED_4_BYTE,
		.elem_len       = 1,
		.elem_size      = sizeof(u32),
		.array_type       = NO_ARRAY,
		.tlv_type       = 0x02,
		.offset         =
		offsetof(struct wlfw_m3_dump_upload_segments_req_ind_msg_v01,
			 no_of_valid_segments),
	},
	{
		.data_type      = QMI_STRUCT,
		.elem_len       = QMI_WLFW_MAX_M3_SEGMENTS_SIZE_V01,
		.elem_size      = sizeof(struct wlfw_m3_segment_info_s_v01),
		.array_type       = STATIC_ARRAY,
		.tlv_type       = 0x03,
		.offset         =
		offsetof(struct wlfw_m3_dump_upload_segments_req_ind_msg_v01,
			 m3_segment),
		.ei_array      = wlfw_m3_segment_info_s_v01_ei,
	},
	{
		.data_type      = QMI_EOTI,
		.array_type       = NO_ARRAY,
		.tlv_type       = QMI_COMMON_TLV_TYPE,
	},
};
+34 −1
Original line number Diff line number Diff line
@@ -69,6 +69,7 @@
#define QMI_WLFW_GET_INFO_RESP_V01 0x004A
#define QMI_WLFW_PCIE_GEN_SWITCH_RESP_V01 0x0053
#define QMI_WLFW_INI_REQ_V01 0x002F
#define QMI_WLFW_M3_DUMP_UPLOAD_SEGMENTS_REQ_IND_V01 0x0054
#define QMI_WLFW_MSA_READY_REQ_V01 0x002E
#define QMI_WLFW_M3_DUMP_UPLOAD_DONE_REQ_V01 0x004E
#define QMI_WLFW_CAP_RESP_V01 0x0024
@@ -96,6 +97,7 @@
#define QMI_WLFW_QDSS_TRACE_FREE_IND_V01 0x0046
#define QMI_WLFW_QDSS_MEM_READY_IND_V01 0x0052

#define QMI_WLFW_MAX_M3_SEGMENTS_SIZE_V01 10
#define QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01 2
#define QMI_WLFW_MAX_NUM_MEM_SEG_V01 32
#define QMI_WLFW_MAX_NUM_CAL_V01 5
@@ -225,6 +227,18 @@ enum wlfw_power_save_mode_v01 {
	WLFW_POWER_SAVE_MODE_MAX_VAL_V01 = INT_MAX,
};

enum wlfw_m3_segment_type_v01 {
	WLFW_M3_SEGMENT_TYPE_MIN_VAL_V01 = INT_MIN,
	QMI_M3_SEGMENT_INVALID_V01 = 0,
	QMI_M3_SEGMENT_PHYAREG_V01 = 1,
	QMI_M3_SEGMENT_PHYDBG_V01 = 2,
	QMI_M3_SEGMENT_WMAC0_REG_V01 = 3,
	QMI_M3_SEGMENT_WCSSDBG_V01 = 4,
	QMI_M3_SEGMENT_PHYAPDMEM_V01 = 5,
	QMI_M3_SEGMENT_MAX_V01 = 6,
	WLFW_M3_SEGMENT_TYPE_MAX_VAL_V01 = INT_MAX,
};

#define QMI_WLFW_CE_ATTR_FLAGS_V01 ((u32)0x00)
#define QMI_WLFW_CE_ATTR_NO_SNOOP_V01 ((u32)0x01)
#define QMI_WLFW_CE_ATTR_BYTE_SWAP_DATA_V01 ((u32)0x02)
@@ -327,6 +341,13 @@ struct wlfw_host_ddr_range_s_v01 {
	u64 size;
};

struct wlfw_m3_segment_info_s_v01 {
	enum wlfw_m3_segment_type_v01 type;
	u64 addr;
	u64 size;
	char name[QMI_WLFW_MAX_STR_LEN_V01 + 1];
};

struct wlfw_ind_register_req_msg_v01 {
	u8 fw_ready_enable_valid;
	u8 fw_ready_enable;
@@ -366,9 +387,11 @@ struct wlfw_ind_register_req_msg_v01 {
	u8 wfc_call_twt_config_enable;
	u8 qdss_mem_ready_enable_valid;
	u8 qdss_mem_ready_enable;
	u8 m3_dump_upload_segments_req_enable_valid;
	u8 m3_dump_upload_segments_req_enable;
};

#define WLFW_IND_REGISTER_REQ_MSG_V01_MAX_MSG_LEN 82
#define WLFW_IND_REGISTER_REQ_MSG_V01_MAX_MSG_LEN 86
extern struct qmi_elem_info wlfw_ind_register_req_msg_v01_ei[];

struct wlfw_ind_register_resp_msg_v01 {
@@ -1145,4 +1168,14 @@ struct wlfw_pcie_gen_switch_resp_msg_v01 {
#define WLFW_PCIE_GEN_SWITCH_RESP_MSG_V01_MAX_MSG_LEN 7
extern struct qmi_elem_info wlfw_pcie_gen_switch_resp_msg_v01_ei[];

struct wlfw_m3_dump_upload_segments_req_ind_msg_v01 {
	u32 pdev_id;
	u32 no_of_valid_segments;
	struct wlfw_m3_segment_info_s_v01
		m3_segment[QMI_WLFW_MAX_M3_SEGMENTS_SIZE_V01];
};

#define WLFW_M3_DUMP_UPLOAD_SEGMENTS_REQ_IND_MSG_V01_MAX_MSG_LEN 387
extern struct qmi_elem_info wlfw_m3_dump_upload_segments_req_ind_msg_v01_ei[];

#endif
+103 −0
Original line number Diff line number Diff line
@@ -58,6 +58,13 @@
#define ICNSS_WLAN_SERVICE_NAME					"wlan/fw"
#define ICNSS_DEFAULT_FEATURE_MASK 0x01

#define ICNSS_M3_SEGMENT(segment)		"wcnss_"segment
#define ICNSS_M3_SEGMENT_PHYAREG		"phyareg"
#define ICNSS_M3_SEGMENT_PHYA			"phydbg"
#define ICNSS_M3_SEGMENT_WMACREG		"wmac0reg"
#define ICNSS_M3_SEGMENT_WCSSDBG		"WCSSDBG"
#define ICNSS_M3_SEGMENT_PHYAM3			"PHYAPDMEM"

#define ICNSS_QUIRKS_DEFAULT		BIT(FW_REJUVENATE_ENABLE)
#define ICNSS_MAX_PROBE_CNT		2

@@ -174,6 +181,8 @@ char *icnss_driver_event_to_str(enum icnss_driver_event_type type)
		return "QDSS_TRACE_SAVE";
	case ICNSS_DRIVER_EVENT_QDSS_TRACE_FREE:
		return "QDSS_TRACE_FREE";
	case ICNSS_DRIVER_EVENT_M3_DUMP_UPLOAD_REQ:
		return "M3_DUMP_UPLOAD";
	case ICNSS_DRIVER_EVENT_MAX:
		return "EVENT_MAX";
	}
@@ -1283,6 +1292,61 @@ static int icnss_qdss_trace_free_hdlr(struct icnss_priv *priv)
	return 0;
}

static int icnss_m3_dump_upload_req_hdlr(struct icnss_priv *priv,
					 void *data)
{
	struct icnss_m3_upload_segments_req_data *event_data = data;
	struct ramdump_segment segment;
	int i, status = 0, ret = 0;

	for (i = 0; i < event_data->no_of_valid_segments; i++) {
		memset(&segment, 0, sizeof(segment));
		segment.v_address = devm_ioremap(&priv->pdev->dev,
						event_data->m3_segment[i].addr,
						event_data->m3_segment[i].size);
		if (!segment.v_address) {
			icnss_pr_err("Failed to ioremap M3 Dump region");
			ret = -ENOMEM;
			goto send_resp;
		}

		segment.size = event_data->m3_segment[i].size;
		segment.name = event_data->m3_segment[i].name;

		switch (event_data->m3_segment[i].type) {
		case QMI_M3_SEGMENT_PHYAREG_V01:
			ret = do_ramdump(priv->m3_dump_dev_seg1, &segment, 1);
			break;
		case QMI_M3_SEGMENT_PHYDBG_V01:
			ret = do_ramdump(priv->m3_dump_dev_seg2, &segment, 1);
			break;
		case QMI_M3_SEGMENT_WMAC0_REG_V01:
			ret = do_ramdump(priv->m3_dump_dev_seg3, &segment, 1);
			break;
		case QMI_M3_SEGMENT_WCSSDBG_V01:
			ret = do_ramdump(priv->m3_dump_dev_seg4, &segment, 1);
			break;
		case QMI_M3_SEGMENT_PHYAPDMEM_V01:
			ret = do_ramdump(priv->m3_dump_dev_seg5, &segment, 1);
			break;
		default:
			icnss_pr_err("Invalid Segment type: %d",
				     event_data->m3_segment[i].type);
		}

		if (ret) {
			status = ret;
			icnss_pr_err("Failed to dump m3 %s segment, err = %d\n",
				     event_data->m3_segment[i].name, ret);
		}
	}
send_resp:
	icnss_wlfw_m3_dump_upload_done_send_sync(priv, event_data->pdev_id,
						 status);

	return ret;
}

static void icnss_driver_event_work(struct work_struct *work)
{
	struct icnss_priv *priv =
@@ -1356,6 +1420,9 @@ static void icnss_driver_event_work(struct work_struct *work)
		case ICNSS_DRIVER_EVENT_QDSS_TRACE_FREE:
			ret = icnss_qdss_trace_free_hdlr(priv);
			break;
		case ICNSS_DRIVER_EVENT_M3_DUMP_UPLOAD_REQ:
			ret = icnss_m3_dump_upload_req_hdlr(priv, event->data);
			break;
		default:
			icnss_pr_err("Invalid Event type: %d", event->type);
			kfree(event);
@@ -1831,6 +1898,36 @@ static int icnss_enable_recovery(struct icnss_priv *priv)
	if (!priv->msa0_dump_dev)
		return -ENOMEM;

	priv->m3_dump_dev_seg1 = create_ramdump_device(
				    ICNSS_M3_SEGMENT(ICNSS_M3_SEGMENT_PHYAREG),
				    &priv->pdev->dev);
	if (!priv->m3_dump_dev_seg1)
		return -ENOMEM;

	priv->m3_dump_dev_seg2 = create_ramdump_device(
				    ICNSS_M3_SEGMENT(ICNSS_M3_SEGMENT_PHYA),
				    &priv->pdev->dev);
	if (!priv->m3_dump_dev_seg2)
		return -ENOMEM;

	priv->m3_dump_dev_seg3 = create_ramdump_device(
				    ICNSS_M3_SEGMENT(ICNSS_M3_SEGMENT_WMACREG),
				    &priv->pdev->dev);
	if (!priv->m3_dump_dev_seg3)
		return -ENOMEM;

	priv->m3_dump_dev_seg4 = create_ramdump_device(
				    ICNSS_M3_SEGMENT(ICNSS_M3_SEGMENT_WCSSDBG),
				    &priv->pdev->dev);
	if (!priv->m3_dump_dev_seg4)
		return -ENOMEM;

	priv->m3_dump_dev_seg5 = create_ramdump_device(
				     ICNSS_M3_SEGMENT(ICNSS_M3_SEGMENT_PHYAM3),
				     &priv->pdev->dev);
	if (!priv->m3_dump_dev_seg5)
		return -ENOMEM;

	icnss_modem_ssr_register_notifier(priv);
	if (test_bit(SSR_ONLY, &priv->ctrl_params.quirks)) {
		icnss_pr_dbg("PDR disabled through module parameter\n");
@@ -3381,6 +3478,12 @@ static int icnss_remove(struct platform_device *pdev)

	destroy_ramdump_device(priv->msa0_dump_dev);

	destroy_ramdump_device(priv->m3_dump_dev_seg1);
	destroy_ramdump_device(priv->m3_dump_dev_seg2);
	destroy_ramdump_device(priv->m3_dump_dev_seg3);
	destroy_ramdump_device(priv->m3_dump_dev_seg4);
	destroy_ramdump_device(priv->m3_dump_dev_seg5);

	icnss_pdr_unregister_notifier(priv);

	icnss_unregister_fw_service(priv);
+6 −0
Original line number Diff line number Diff line
@@ -55,6 +55,7 @@ enum icnss_driver_event_type {
	ICNSS_DRIVER_EVENT_QDSS_TRACE_REQ_MEM,
	ICNSS_DRIVER_EVENT_QDSS_TRACE_SAVE,
	ICNSS_DRIVER_EVENT_QDSS_TRACE_FREE,
	ICNSS_DRIVER_EVENT_M3_DUMP_UPLOAD_REQ,
	ICNSS_DRIVER_EVENT_MAX,
};

@@ -389,6 +390,11 @@ struct icnss_priv {
	uint8_t *diag_reg_read_buf;
	atomic_t pm_count;
	struct ramdump_device *msa0_dump_dev;
	struct ramdump_device *m3_dump_dev_seg1;
	struct ramdump_device *m3_dump_dev_seg2;
	struct ramdump_device *m3_dump_dev_seg3;
	struct ramdump_device *m3_dump_dev_seg4;
	struct ramdump_device *m3_dump_dev_seg5;
	bool force_err_fatal;
	bool allow_recursive_recovery;
	bool early_crash_ind;
+144 −0
Original line number Diff line number Diff line
@@ -44,6 +44,7 @@
#define DUMMY_BDF_FILE_NAME		"bdwlan.dmy"

#define DEVICE_BAR_SIZE			0x200000
#define M3_SEGMENT_ADDR_MASK		0xFFFFFFFF

#ifdef CONFIG_ICNSS2_DEBUG
bool ignore_fw_timeout;
@@ -539,6 +540,8 @@ int wlfw_ind_register_send_sync_msg(struct icnss_priv *priv)
		req->qdss_trace_free_enable = 1;
		req->respond_get_info_enable_valid = 1;
		req->respond_get_info_enable = 1;
		req->m3_dump_upload_segments_req_enable_valid = 1;
		req->m3_dump_upload_segments_req_enable = 1;
	}

	priv->stats.ind_register_req++;
@@ -1560,6 +1563,67 @@ int wlfw_qdss_trace_mem_info_send_sync(struct icnss_priv *priv)
	return ret;
}

int icnss_wlfw_m3_dump_upload_done_send_sync(struct icnss_priv *priv,
					     u32 pdev_id, int status)
{
	struct wlfw_m3_dump_upload_done_req_msg_v01 *req;
	struct wlfw_m3_dump_upload_done_resp_msg_v01 *resp;
	struct qmi_txn txn;
	int ret = 0;

	req = kzalloc(sizeof(*req), GFP_KERNEL);
	if (!req)
		return -ENOMEM;

	resp = kzalloc(sizeof(*resp), GFP_KERNEL);
	if (!resp) {
		kfree(req);
		return -ENOMEM;
	}

	icnss_pr_dbg("Sending M3 Upload done req, pdev %d, status %d\n",
		     pdev_id, status);

	req->pdev_id = pdev_id;
	req->status = status;

	ret = qmi_txn_init(&priv->qmi, &txn,
			   wlfw_m3_dump_upload_done_resp_msg_v01_ei, resp);
	if (ret < 0) {
		icnss_pr_err("Fail to initialize txn for M3 dump upload done req: err %d\n",
			     ret);
		goto out;
	}

	ret = qmi_send_request(&priv->qmi, NULL, &txn,
			       QMI_WLFW_M3_DUMP_UPLOAD_DONE_REQ_V01,
			       WLFW_M3_DUMP_UPLOAD_DONE_REQ_MSG_V01_MAX_MSG_LEN,
			       wlfw_m3_dump_upload_done_req_msg_v01_ei, req);
	if (ret < 0) {
		qmi_txn_cancel(&txn);
		icnss_pr_err("Fail to send M3 dump upload done request: err %d\n",
			     ret);
		goto out;
	}

	ret = qmi_txn_wait(&txn, priv->ctrl_params.qmi_timeout);
	if (ret < 0) {
		icnss_pr_err("Fail to wait for response of M3 dump upload done request, err %d\n",
			     ret);
		goto out;
	} else if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
		icnss_pr_err("M3 Dump Upload Done Req failed, result: %d, err: 0x%X\n",
			     resp->resp.result, resp->resp.error);
		ret = -resp->resp.result;
		goto out;
	}

out:
	kfree(req);
	kfree(resp);
	return ret;
}

static void fw_ready_ind_cb(struct qmi_handle *qmi, struct sockaddr_qrtr *sq,
			    struct qmi_txn *txn, const void *data)
{
@@ -1951,6 +2015,78 @@ static void icnss_wlfw_respond_get_info_ind_cb(struct qmi_handle *qmi,
				       ind_msg->data_len);
}

static void icnss_wlfw_m3_dump_upload_segs_req_ind_cb(struct qmi_handle *qmi,
						      struct sockaddr_qrtr *sq,
						      struct qmi_txn *txn,
						      const void *d)
{
	struct icnss_priv *priv = container_of(qmi, struct icnss_priv, qmi);
	const struct wlfw_m3_dump_upload_segments_req_ind_msg_v01 *ind_msg = d;
	struct icnss_m3_upload_segments_req_data *event_data = NULL;
	u64 max_mapped_addr = 0;
	u64 segment_addr = 0;
	int i = 0;

	icnss_pr_dbg("Received QMI WLFW M3 dump upload sigments indication\n");

	if (!txn) {
		icnss_pr_err("Spurious indication\n");
		return;
	}

	icnss_pr_dbg("M3 Dump upload info: pdev_id: %d no_of_segments: %d\n",
		     ind_msg->pdev_id, ind_msg->no_of_valid_segments);

	if (ind_msg->no_of_valid_segments > QMI_WLFW_MAX_M3_SEGMENTS_SIZE_V01)
		return;

	event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
	if (!event_data)
		return;

	event_data->pdev_id = ind_msg->pdev_id;
	event_data->no_of_valid_segments = ind_msg->no_of_valid_segments;
	max_mapped_addr = priv->msa_pa + priv->msa_mem_size;

	for (i = 0; i < ind_msg->no_of_valid_segments; i++) {
		segment_addr = ind_msg->m3_segment[i].addr &
				M3_SEGMENT_ADDR_MASK;

		if (ind_msg->m3_segment[i].size > priv->msa_mem_size ||
		    segment_addr >= max_mapped_addr ||
		    segment_addr < priv->msa_pa ||
		    ind_msg->m3_segment[i].size +
		    segment_addr > max_mapped_addr) {
			icnss_pr_dbg("Received out of range Segment %d Addr: 0x%llx Size: 0x%x, Name: %s, type: %d\n",
				     (i + 1), segment_addr,
				     ind_msg->m3_segment[i].size,
				     ind_msg->m3_segment[i].name,
				     ind_msg->m3_segment[i].type);
			goto out;
		}

		event_data->m3_segment[i].addr = segment_addr;
		event_data->m3_segment[i].size = ind_msg->m3_segment[i].size;
		event_data->m3_segment[i].type = ind_msg->m3_segment[i].type;
		strlcpy(event_data->m3_segment[i].name,
			ind_msg->m3_segment[i].name,
			WLFW_MAX_STR_LEN + 1);

		icnss_pr_dbg("Received Segment %d Addr: 0x%llx Size: 0x%x, Name: %s, type: %d\n",
			     (i + 1), segment_addr,
			     ind_msg->m3_segment[i].size,
			     ind_msg->m3_segment[i].name,
			     ind_msg->m3_segment[i].type);
	}

	icnss_driver_event_post(priv, ICNSS_DRIVER_EVENT_M3_DUMP_UPLOAD_REQ,
				0, event_data);

	return;
out:
	kfree(event_data);
}

static struct qmi_msg_handler wlfw_msg_handlers[] = {
	{
		.type = QMI_INDICATION,
@@ -2027,6 +2163,14 @@ static struct qmi_msg_handler wlfw_msg_handlers[] = {
		sizeof(struct wlfw_respond_get_info_ind_msg_v01),
		.fn = icnss_wlfw_respond_get_info_ind_cb
	},
	{
		.type = QMI_INDICATION,
		.msg_id = QMI_WLFW_M3_DUMP_UPLOAD_SEGMENTS_REQ_IND_V01,
		.ei = wlfw_m3_dump_upload_segments_req_ind_msg_v01_ei,
		.decoded_size =
		sizeof(struct wlfw_m3_dump_upload_segments_req_ind_msg_v01),
		.fn = icnss_wlfw_m3_dump_upload_segs_req_ind_cb
	},
	{}
};

Loading