--- a/drivers/net/wireless/ath/ath11k/qmi.c +++ b/drivers/net/wireless/ath/ath11k/qmi.c @@ -8,6 +8,7 @@ #include "debug.h" #include #include +#include /* set the default max assoc sta to max supported by driver */ bool enable_cold_boot_cal = 0; @@ -507,6 +508,24 @@ static struct qmi_elem_info qmi_wlanfw_h .ei_array = qmi_response_type_v01_ei, }, { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x20, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + m3_dump_upload_req_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x20, + .offset = offsetof(struct qmi_wlanfw_ind_register_req_msg_v01, + m3_dump_upload_req_enable), + }, + { .data_type = QMI_EOTI, .array_type = NO_ARRAY, .tlv_type = QMI_COMMON_TLV_TYPE, @@ -1916,6 +1935,87 @@ static struct qmi_elem_info qmi_wlanfw_c }, }; +static struct qmi_elem_info qmi_wlanfw_m3_dump_upload_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 qmi_wlanfw_m3_dump_upload_req_ind_msg_v01, + pdev_id), + }, + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(u64), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct qmi_wlanfw_m3_dump_upload_req_ind_msg_v01, + addr), + }, + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(u64), + .array_type = NO_ARRAY, + .tlv_type = 0x03, + .offset = offsetof(struct qmi_wlanfw_m3_dump_upload_req_ind_msg_v01, + size), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_m3_dump_upload_done_req_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 + qmi_wlanfw_m3_dump_upload_done_req_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 + qmi_wlanfw_m3_dump_upload_done_req_msg_v01, + status), + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct qmi_elem_info qmi_wlanfw_m3_dump_upload_done_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct qmi_wlanfw_m3_dump_upload_done_resp_msg_v01, + resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_EOTI, + .array_type = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + int wlfw_send_qdss_trace_config_download_req(struct ath11k_base *ab, const u8 *buffer, unsigned int file_len) { @@ -2171,6 +2271,8 @@ static int ath11k_qmi_fw_ind_register_se req->qdss_trace_free_enable = 1; req->pin_connect_result_enable_valid = 0; req->pin_connect_result_enable = 0; + req->m3_dump_upload_req_enable_valid = 1; + req->m3_dump_upload_req_enable = 1; ret = qmi_txn_init(handle, &txn, qmi_wlanfw_ind_register_resp_msg_v01_ei, resp); @@ -2327,6 +2429,22 @@ static int ath11k_qmi_assign_target_mem_ ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type; idx++; break; + case M3_DUMP_REGION_TYPE: + if (!ab->qmi.target_mem[i].size) { + ath11k_info(ab, "qmi mem size is zero\n"); + return -EINVAL; + } + if (ab->qmi.target_mem[i].size > ATH11K_QMI_M3_DUMP_SIZE) { + ath11k_warn(ab, "qmi mem size is low to dump m3 ssr\n"); + return -EINVAL; + } + ab->qmi.target_mem[idx].size = ab->qmi.target_mem[i].size; + ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type; + ab->qmi.target_mem[idx].paddr = ab->hw_params.m3_addr; + ab->qmi.target_mem[idx].vaddr = + (unsigned int)ab->qmi.target_mem[idx].paddr; + idx++; + break; default: ath11k_warn(ab, "qmi ignore invalid mem req type %d\n", ab->qmi.target_mem[i].type); @@ -2889,6 +3007,123 @@ int ath11k_qmi_process_coldboot_calibrat return 0; } +static int ath11k_qmi_m3_dump_upload_done_ind_send(struct ath11k_base *ab, + u32 pdev_id, int status) +{ + struct qmi_wlanfw_m3_dump_upload_done_req_msg_v01 *req; + struct qmi_wlanfw_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; + } + + req->pdev_id = pdev_id; + req->status = status; + + ret = qmi_txn_init(&ab->qmi.handle, &txn, + qmi_wlanfw_m3_dump_upload_done_resp_msg_v01_ei, resp); + if (ret < 0) + goto out; + + ret = + qmi_send_request(&ab->qmi.handle, NULL, &txn, + QMI_WLFW_M3_DUMP_UPLOAD_DONE_REQ_V01, + QMI_WLANFW_M3_DUMP_UPLOAD_DONE_REQ_MSG_V01_MAX_MSG_LEN, + qmi_wlanfw_m3_dump_upload_done_req_msg_v01_ei, req); + if (ret < 0) { + qmi_txn_cancel(&txn); + ath11k_warn(ab, "Failed to send M3 dump upload done request, err %d\n", + ret); + goto out; + } + + ret = qmi_txn_wait(&txn, msecs_to_jiffies(ATH11K_QMI_WLANFW_TIMEOUT_MS)); + if (ret < 0) + goto out; + + if (resp->resp.result != QMI_RESULT_SUCCESS_V01) { + ath11k_warn(ab, "qmi M3 upload done req failed, result: %d, err: %d\n", + resp->resp.result, resp->resp.error); + ret = -EINVAL; + goto out; + } + ath11k_info(ab, "qmi m3 dump uploaded\n"); + +out: + kfree(req); + kfree(resp); + return ret; +} + +static void ath11k_qmi_event_m3_dump_upload_req(struct ath11k_qmi *qmi, + void *data) +{ + struct ath11k_base *ab = qmi->ab; + struct target_mem_chunk *target_mem = ab->qmi.target_mem; + struct ath11k_qmi_m3_dump_upload_req_data *event_data = data; + struct ath11k_qmi_m3_dump_data *m3_dump_data; + void *dump; + int i, ret = 0; + + m3_dump_data = kzalloc(sizeof(*m3_dump_data), GFP_KERNEL); + if (!m3_dump_data) + return; + + dump = vzalloc(event_data->size); + if (!dump) { + kfree(m3_dump_data); + return; + } + + for (i = 0; i < ab->qmi.mem_seg_count; i++) { + if (target_mem[i].paddr == event_data->addr && + event_data->size <= target_mem[i].size) + break; + } + + if (i == ab->qmi.mem_seg_count) { + ath11k_warn(ab, "qmi invalid paddr from firmware for M3 dump\n"); + ret = -EINVAL; + vfree(dump); + goto send_resp; + } + + m3_dump_data->addr = ioremap(target_mem[i].paddr, target_mem[i].size); + if (!m3_dump_data->addr) { + ath11k_warn(ab, "qmi failed to ioremap M3 dump region\n"); + ret = -ENOMEM; + vfree(dump); + goto send_resp; + } + + m3_dump_data->size = event_data->size; + m3_dump_data->pdev_id = event_data->pdev_id; + m3_dump_data->timestamp = ktime_to_ms(ktime_get()); + + memcpy(dump, m3_dump_data->addr, m3_dump_data->size); + + dev_coredumpv(ab->dev, dump, le32_to_cpu(m3_dump_data->size), + GFP_KERNEL); + + iounmap(m3_dump_data->addr); + +send_resp: + ret = ath11k_qmi_m3_dump_upload_done_ind_send(ab, event_data->pdev_id, ret); + if (ret < 0) + ath11k_warn(ab, "qmi M3 dump upload done failed\n"); + + kfree(m3_dump_data); + return; +} + static int ath11k_qmi_driver_event_post(struct ath11k_qmi *qmi, enum ath11k_qmi_event_type type, @@ -3042,6 +3277,30 @@ static void ath11k_qmi_msg_cold_boot_cal ath11k_dbg(ab, ATH11K_DBG_QMI, "qmi cold boot calibration done\n"); } +static void ath11k_qmi_m3_dump_upload_req_ind_cb(struct qmi_handle *qmi_hdl, + struct sockaddr_qrtr *sq, + struct qmi_txn *txn, + const void *data) +{ + struct ath11k_qmi *qmi = container_of(qmi_hdl, struct ath11k_qmi, handle); + struct ath11k_base *ab = qmi->ab; + const struct qmi_wlanfw_m3_dump_upload_req_ind_msg_v01 *msg = data; + struct ath11k_qmi_m3_dump_upload_req_data *event_data; + + ath11k_dbg(ab, ATH11K_DBG_QMI, "qmi m3 dump memory request\n"); + + event_data = kzalloc(sizeof(*event_data), GFP_KERNEL); + if (!event_data) + return; + + event_data->pdev_id = msg->pdev_id; + event_data->addr = msg->addr; + event_data->size = msg->size; + + ath11k_qmi_driver_event_post(qmi, ATH11K_QMI_EVENT_M3_DUMP_UPLOAD_REQ, + event_data); +} + static const struct qmi_msg_handler ath11k_qmi_msg_handlers[] = { { .type = QMI_INDICATION, @@ -3072,6 +3331,14 @@ static const struct qmi_msg_handler ath1 sizeof(struct qmi_wlanfw_fw_cold_cal_done_ind_msg_v01), .fn = ath11k_qmi_msg_cold_boot_cal_done_cb, }, + { + .type = QMI_INDICATION, + .msg_id = QMI_WLFW_M3_DUMP_UPLOAD_REQ_IND_V01, + .ei = qmi_wlanfw_m3_dump_upload_req_ind_msg_v01_ei, + .decoded_size = + sizeof(struct qmi_wlanfw_m3_dump_upload_req_ind_msg_v01), + .fn = ath11k_qmi_m3_dump_upload_req_ind_cb, + }, }; static int ath11k_qmi_ops_new_server(struct qmi_handle *qmi_hdl, @@ -3162,6 +3429,9 @@ static void ath11k_qmi_driver_event_work break; case ATH11K_QMI_EVENT_COLD_BOOT_CAL_DONE: break; + case ATH11K_QMI_EVENT_M3_DUMP_UPLOAD_REQ: + ath11k_qmi_event_m3_dump_upload_req(qmi, event->data); + break; default: ath11k_warn(ab, "invalid event type: %d", event->type); break; --- a/drivers/net/wireless/ath/ath11k/qmi.h +++ b/drivers/net/wireless/ath/ath11k/qmi.h @@ -36,11 +36,16 @@ #define ATH11K_QMI_DEFAULT_QDSS_CONFIG_FILE_NAME "qdss_trace_config.bin" #define NODE_ID_BASE 0x27 #define FW_ID_BASE 7 +#define ATH11K_QMI_IPQ8074_M3_DUMP_ADDRESS 0x51000000 +#define ATH11K_QMI_IPQ6018_M3_DUMP_ADDRESS 0x50100000 +#define ATH11K_QMI_M3_DUMP_SIZE 0x100000 #define QMI_WLFW_REQUEST_MEM_IND_V01 0x0035 #define QMI_WLFW_FW_MEM_READY_IND_V01 0x0037 #define QMI_WLFW_COLD_BOOT_CAL_DONE_IND_V01 0x0021 #define QMI_WLFW_FW_READY_IND_V01 0x0038 +#define QMI_WLFW_M3_DUMP_UPLOAD_DONE_REQ_V01 0x004E +#define QMI_WLFW_M3_DUMP_UPLOAD_REQ_IND_V01 0x004D #define QMI_WLANFW_MAX_DATA_SIZE_V01 6144 #define ATH11K_FIRMWARE_MODE_OFF 4 @@ -71,6 +76,7 @@ enum ath11k_qmi_event_type { ATH11K_QMI_EVENT_FORCE_FW_ASSERT, ATH11K_QMI_EVENT_POWER_UP, ATH11K_QMI_EVENT_POWER_DOWN, + ATH11K_QMI_EVENT_M3_DUMP_UPLOAD_REQ, ATH11K_QMI_EVENT_MAX, }; @@ -80,6 +86,13 @@ struct ath11k_qmi_driver_event { void *data; }; +struct ath11k_qmi_m3_dump_data { + u32 pdev_id; + u32 size; + u64 timestamp; + char *addr; +}; + struct ath11k_qmi_ce_cfg { const struct ce_pipe_config *tgt_ce; int tgt_ce_len; @@ -140,6 +153,12 @@ struct ath11k_qmi { unsigned int service_ins_id; }; +struct ath11k_qmi_m3_dump_upload_req_data { + u32 pdev_id; + u64 addr; + u64 size; +}; + #define QMI_WLANFW_QDSS_TRACE_CONFIG_DOWNLOAD_REQ_MSG_V01_MAX_LEN 6167 #define QMI_WLANFW_QDSS_TRACE_CONFIG_DOWNLOAD_RESP_MSG_V01_MAX_LEN 7 #define QMI_WLANFW_QDSS_TRACE_CONFIG_DOWNLOAD_REQ_V01 0x0044 @@ -161,6 +180,15 @@ struct qmi_wlanfw_qdss_trace_config_down struct qmi_response_type_v01 resp; }; +struct qmi_wlanfw_m3_dump_upload_done_req_msg_v01 { + u32 pdev_id; + u32 status; +}; + +struct qmi_wlanfw_m3_dump_upload_done_resp_msg_v01 { + struct qmi_response_type_v01 resp; +}; + #define QMI_WLANFW_QDSS_TRACE_MODE_REQ_V01 0x0045 #define QMI_WLANFW_QDSS_TRACE_MODE_REQ_MSG_V01_MAX_LEN 18 #define QMI_WLANFW_QDSS_TRACE_MODE_RESP_MSG_V01_MAX_LEN 7 @@ -192,6 +220,7 @@ struct qmi_wlanfw_qdss_trace_mode_resp_m #define QMI_IPQ8074_FW_MEM_MODE 0xFF #define HOST_DDR_REGION_TYPE 0x1 #define BDF_MEM_REGION_TYPE 0x2 +#define M3_DUMP_REGION_TYPE 0x3 #define CALDB_MEM_REGION_TYPE 0x4 #define QMI_WLFW_MAX_PLATFORM_NAME_LEN_V01 64 @@ -349,6 +378,12 @@ struct qmi_wlanfw_fw_cold_cal_done_ind_m char placeholder; }; +struct qmi_wlanfw_m3_dump_upload_req_ind_msg_v01 { + u32 pdev_id; + u64 addr; + u64 size; +}; + #define QMI_WLANFW_CAP_REQ_MSG_V01_MAX_LEN 0 #define QMI_WLANFW_CAP_RESP_MSG_V01_MAX_LEN 235 #define QMI_WLANFW_CAP_REQ_V01 0x0024 @@ -479,6 +514,8 @@ struct qmi_wlanfw_bdf_download_resp_msg_ #define QMI_WLANFW_M3_INFO_RESP_V01 0x003C #define QMI_WLANFW_M3_INFO_REQ_V01 0x003C +#define QMI_WLANFW_M3_DUMP_UPLOAD_DONE_REQ_MSG_V01_MAX_MSG_LEN 14 + struct qmi_wlanfw_m3_info_req_msg_v01 { u64 addr; u32 size; --- a/drivers/net/wireless/ath/ath11k/core.h +++ b/drivers/net/wireless/ath/ath11k/core.h @@ -453,6 +453,7 @@ struct ath11k_debug { bool is_pkt_logging; #endif u32 rx_filter; + bool enable_m3_dump; }; struct ath11k_per_peer_tx_stats { --- a/drivers/net/wireless/ath/ath11k/debug.c +++ b/drivers/net/wireless/ath/ath11k/debug.c @@ -1715,6 +1715,68 @@ static const struct file_operations fops .open = simple_open }; +static ssize_t ath11k_write_enable_m3_dump(struct file *file, + const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct ath11k *ar = file->private_data; + bool enable; + int ret; + + if (kstrtobool_from_user(ubuf, count, &enable)) + return -EINVAL; + + mutex_lock(&ar->conf_mutex); + + if (ar->state != ATH11K_STATE_ON) { + ret = -ENETDOWN; + goto exit; + } + + if (enable == ar->debug.enable_m3_dump) { + ret = count; + goto exit; + } + + ret = ath11k_wmi_pdev_m3_dump_enable(ar, enable); + if (ret) { + ath11k_warn(ar->ab, + "failed to enable m3 ssr dump %d\n", + ret); + goto exit; + } + + ar->debug.enable_m3_dump = enable; + ret = count; + +exit: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static ssize_t ath11k_read_enable_m3_dump(struct file *file, + char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct ath11k *ar = file->private_data; + char buf[32]; + size_t len = 0; + + mutex_lock(&ar->conf_mutex); + len = scnprintf(buf, sizeof(buf) - len, "%d\n", + ar->debug.enable_m3_dump); + mutex_unlock(&ar->conf_mutex); + + return simple_read_from_buffer(ubuf, count, ppos, buf, len); + +} + +static const struct file_operations fops_enable_m3_dump = { + .read = ath11k_read_enable_m3_dump, + .write = ath11k_write_enable_m3_dump, + .open = simple_open +}; + int ath11k_debug_register(struct ath11k *ar) { struct ath11k_base *ab = ar->ab; @@ -1771,6 +1833,10 @@ int ath11k_debug_register(struct ath11k &ar->dfs_block_radar_events); } + debugfs_create_file("enable_m3_dump", 0644, + ar->debug.debugfs_pdev, ar, + &fops_enable_m3_dump); + return 0; } --- a/drivers/net/wireless/ath/ath11k/wmi.c +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -7181,6 +7181,36 @@ int ath11k_wmi_simulate_radar(struct ath return ath11k_wmi_send_unit_test_cmd(ar, wmi_ut, dfs_args); } +int ath11k_wmi_pdev_m3_dump_enable(struct ath11k *ar, u32 enable) { + struct ath11k_vif *arvif; + u32 m3_args[WMI_M3_MAX_TEST_ARGS]; + struct wmi_unit_test_cmd wmi_ut; + bool arvif_found = false; + + list_for_each_entry(arvif, &ar->arvifs, list) { + if (arvif->is_started) { + arvif_found = true; + break; + } + } + + if (!arvif_found) + return -EINVAL; + + m3_args[WMI_M3_TEST_CMDID] = WMI_DBG_ENABLE_M3_SSR; + m3_args[WMI_M3_TEST_ENABLE] = enable; + + wmi_ut.vdev_id = arvif->vdev_id; + wmi_ut.module_id = WMI_M3_UNIT_TEST_MODULE; + wmi_ut.num_args = WMI_M3_MAX_TEST_ARGS; + wmi_ut.diag_token = WMI_M3_UNIT_TEST_TOKEN; + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "%s M3 SSR dump\n", + enable ? "Enabling" : "Disabling"); + + return ath11k_wmi_send_unit_test_cmd(ar, wmi_ut, m3_args); +} + int ath11k_wmi_connect(struct ath11k_base *ab) { u32 i; --- a/drivers/net/wireless/ath/ath11k/wmi.h +++ b/drivers/net/wireless/ath/ath11k/wmi.h @@ -2055,6 +2055,11 @@ enum wmi_tlv_service { }; +enum wmi_unit_test_cmdid { + /* TODO: Add the remaining cmd ids if needed */ + WMI_DBG_ENABLE_M3_SSR = 36, +}; + enum { WMI_SMPS_FORCED_MODE_NONE = 0, WMI_SMPS_FORCED_MODE_DISABLED, @@ -3884,6 +3889,15 @@ struct wmi_dfs_unit_test_arg { u32 radar_param; }; +#define WMI_M3_UNIT_TEST_MODULE 0x22 +#define WMI_M3_UNIT_TEST_TOKEN 0 + +enum wmi_m3_test_args_idx { + WMI_M3_TEST_CMDID, + WMI_M3_TEST_ENABLE, + WMI_M3_MAX_TEST_ARGS, +}; + struct wmi_unit_test_cmd { u32 tlv_header; u32 vdev_id; @@ -5375,4 +5389,5 @@ int ath11k_wmi_send_obss_color_collision int ath11k_wmi_send_bss_color_change_enable_cmd(struct ath11k *ar, u32 vdev_id, bool enable); int ath11k_wmi_pdev_lro_cfg(struct ath11k *ar, int pdev_id); +int ath11k_wmi_pdev_m3_dump_enable(struct ath11k *ar, u32 enable); #endif --- a/drivers/net/wireless/ath/ath11k/core.c +++ b/drivers/net/wireless/ath/ath11k/core.c @@ -47,6 +47,7 @@ static const struct ath11k_hw_params ath .hal_desc_sz = sizeof(struct hal_rx_desc_ipq8074), .hw_ops = &ath11k_qca8074_ops, .credit_flow = false, + .m3_addr = ATH11K_QMI_IPQ8074_M3_DUMP_ADDRESS, }, { .dev_id = ATH11K_HW_IPQ6018, @@ -65,6 +66,7 @@ static const struct ath11k_hw_params ath .hal_desc_sz = sizeof(struct hal_rx_desc_ipq8074), .hw_ops = &ath11k_qca6018_ops, .credit_flow = false, + .m3_addr = ATH11K_QMI_IPQ6018_M3_DUMP_ADDRESS, }, { .dev_id = ATH11K_HW_QCN90XX, --- a/drivers/net/wireless/ath/ath11k/hw.h +++ b/drivers/net/wireless/ath/ath11k/hw.h @@ -173,6 +173,7 @@ struct ath11k_hw_params { u8 spectral_fft_sz; bool credit_flow; u32 hal_desc_sz; + u32 m3_addr; const struct ath11k_hw_ops *hw_ops; };