Loading drivers/net/wireless/cnss_utils/wlan_firmware_service_v01.c +102 −0 Original line number Diff line number Diff line Loading @@ -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, Loading Loading @@ -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, Loading Loading @@ -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, }, }; drivers/net/wireless/cnss_utils/wlan_firmware_service_v01.h +34 −1 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 Loading Loading @@ -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) Loading Loading @@ -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; Loading Loading @@ -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 { Loading Loading @@ -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 drivers/soc/qcom/icnss2/main.c +103 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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"; } Loading Loading @@ -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 = Loading Loading @@ -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); Loading Loading @@ -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"); Loading Loading @@ -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); Loading drivers/soc/qcom/icnss2/main.h +6 −0 Original line number Diff line number Diff line Loading @@ -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, }; Loading Loading @@ -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; Loading drivers/soc/qcom/icnss2/qmi.c +144 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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++; Loading Loading @@ -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) { Loading Loading @@ -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, Loading Loading @@ -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 Loading
drivers/net/wireless/cnss_utils/wlan_firmware_service_v01.c +102 −0 Original line number Diff line number Diff line Loading @@ -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, Loading Loading @@ -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, Loading Loading @@ -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, }, };
drivers/net/wireless/cnss_utils/wlan_firmware_service_v01.h +34 −1 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 Loading Loading @@ -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) Loading Loading @@ -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; Loading Loading @@ -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 { Loading Loading @@ -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
drivers/soc/qcom/icnss2/main.c +103 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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"; } Loading Loading @@ -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 = Loading Loading @@ -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); Loading Loading @@ -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"); Loading Loading @@ -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); Loading
drivers/soc/qcom/icnss2/main.h +6 −0 Original line number Diff line number Diff line Loading @@ -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, }; Loading Loading @@ -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; Loading
drivers/soc/qcom/icnss2/qmi.c +144 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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++; Loading Loading @@ -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) { Loading Loading @@ -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, Loading Loading @@ -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