Loading drivers/soc/qcom/icnss2/main.c +210 −10 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2015-2020, The Linux Foundation. All rights reserved. * Copyright (c) 2015-2020, 2021, The Linux Foundation. * All rights reserved. */ #define pr_fmt(fmt) "icnss2: " fmt Loading Loading @@ -74,7 +75,7 @@ #define WLFW_TIMEOUT msecs_to_jiffies(3000) static struct icnss_priv *penv; static struct work_struct wpss_loader; uint64_t dynamic_feature_mask = ICNSS_DEFAULT_FEATURE_MASK; #define ICNSS_EVENT_PENDING 2989 Loading Loading @@ -183,6 +184,8 @@ char *icnss_driver_event_to_str(enum icnss_driver_event_type type) return "QDSS_TRACE_FREE"; case ICNSS_DRIVER_EVENT_M3_DUMP_UPLOAD_REQ: return "M3_DUMP_UPLOAD"; case ICNSS_DRIVER_EVENT_QDSS_TRACE_REQ_DATA: return "QDSS_TRACE_REQ_DATA"; case ICNSS_DRIVER_EVENT_MAX: return "EVENT_MAX"; } Loading Loading @@ -833,6 +836,9 @@ static int icnss_driver_event_fw_init_done(struct icnss_priv *priv, void *data) icnss_pr_info("WLAN FW Initialization done: 0x%lx\n", priv->state); if (icnss_wlfw_qdss_dnld_send_sync(priv)) icnss_pr_info("Failed to download qdss configuration file"); if (test_bit(ICNSS_COLD_BOOT_CAL, &priv->state)) ret = wlfw_wlan_mode_send_sync_msg(priv, (enum wlfw_driver_mode_enum_v01)ICNSS_CALIBRATION); Loading Loading @@ -1008,6 +1014,25 @@ static inline int icnss_atomic_dec_if_greater_one(atomic_t *v) return dec; } static int icnss_qdss_trace_req_data_hdlr(struct icnss_priv *priv, void *data) { int ret = 0; struct icnss_qmi_event_qdss_trace_save_data *event_data = data; if (!priv) return -ENODEV; if (!data) return -EINVAL; ret = icnss_wlfw_qdss_data_send_sync(priv, event_data->file_name, event_data->total_size); kfree(data); return ret; } static int icnss_event_soc_wake_request(struct icnss_priv *priv, void *data) { int ret = 0; Loading Loading @@ -1432,6 +1457,9 @@ static void icnss_driver_event_work(struct work_struct *work) break; case ICNSS_DRIVER_EVENT_M3_DUMP_UPLOAD_REQ: ret = icnss_m3_dump_upload_req_hdlr(priv, event->data); case ICNSS_DRIVER_EVENT_QDSS_TRACE_REQ_DATA: ret = icnss_qdss_trace_req_data_hdlr(priv, event->data); break; default: icnss_pr_err("Invalid Event type: %d", event->type); Loading Loading @@ -2968,27 +2996,31 @@ void icnss_disallow_recursive_recovery(struct device *dev) icnss_pr_info("Recursive recovery disallowed for WLAN\n"); } static void icnss_sysfs_create(struct icnss_priv *priv) static int icnss_create_shutdown_sysfs(struct icnss_priv *priv) { struct kobject *icnss_kobject; int error = 0; int ret = 0; atomic_set(&priv->is_shutdown, false); icnss_kobject = kobject_create_and_add("shutdown_wlan", kernel_kobj); if (!icnss_kobject) { icnss_pr_err("Unable to create kernel object"); return; icnss_pr_err("Unable to create shutdown_wlan kernel object"); return -EINVAL; } priv->icnss_kobject = icnss_kobject; error = sysfs_create_file(icnss_kobject, &icnss_sysfs_attribute.attr); if (error) icnss_pr_err("Unable to create icnss sysfs file"); ret = sysfs_create_file(icnss_kobject, &icnss_sysfs_attribute.attr); if (ret) { icnss_pr_err("Unable to create icnss sysfs file err:%d", ret); return ret; } static void icnss_sysfs_destroy(struct icnss_priv *priv) return ret; } static void icnss_destroy_shutdown_sysfs(struct icnss_priv *priv) { struct kobject *icnss_kobject; Loading @@ -2997,6 +3029,173 @@ static void icnss_sysfs_destroy(struct icnss_priv *priv) kobject_put(icnss_kobject); } static ssize_t qdss_tr_start_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct icnss_priv *priv = dev_get_drvdata(dev); wlfw_qdss_trace_start(priv); icnss_pr_dbg("Received QDSS start command\n"); return count; } static ssize_t qdss_tr_stop_store(struct device *dev, struct device_attribute *attr, const char *user_buf, size_t count) { struct icnss_priv *priv = dev_get_drvdata(dev); u32 option = 0; if (sscanf(user_buf, "%du", &option) != 1) return -EINVAL; wlfw_qdss_trace_stop(priv, option); icnss_pr_dbg("Received QDSS stop command\n"); return count; } static ssize_t qdss_conf_download_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct icnss_priv *priv = dev_get_drvdata(dev); icnss_wlfw_qdss_dnld_send_sync(priv); icnss_pr_dbg("Received QDSS download config command\n"); return count; } static ssize_t hw_trc_override_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct icnss_priv *priv = dev_get_drvdata(dev); int tmp = 0; if (sscanf(buf, "%du", &tmp) != 1) return -EINVAL; priv->hw_trc_override = tmp; icnss_pr_dbg("Received QDSS hw_trc_override indication\n"); return count; } static void icnss_wpss_load(struct work_struct *wpss_load_work) { struct icnss_priv *priv = icnss_get_plat_priv(); priv->subsys = subsystem_get("wpss"); if (IS_ERR(priv->subsys)) icnss_pr_err("Failed to load wpss subsys"); } static inline void icnss_wpss_unload(struct icnss_priv *priv) { if (priv->subsys) { subsystem_put(priv->subsys); priv->subsys = NULL; } } static ssize_t wpss_boot_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct icnss_priv *priv = dev_get_drvdata(dev); int wpss_subsys = 0; if (priv->device_id != WCN6750_DEVICE_ID) return count; if (sscanf(buf, "%du", &wpss_subsys) != 1) { icnss_pr_err("Failed to read wpss_subsys info"); return -EINVAL; } icnss_pr_dbg("WPSS Subsystem: %s", wpss_subsys ? "GET" : "PUT"); if (wpss_subsys == 1) schedule_work(&wpss_loader); else if (wpss_subsys == 0) icnss_wpss_unload(priv); return count; } static DEVICE_ATTR_WO(qdss_tr_start); static DEVICE_ATTR_WO(qdss_tr_stop); static DEVICE_ATTR_WO(qdss_conf_download); static DEVICE_ATTR_WO(hw_trc_override); static DEVICE_ATTR_WO(wpss_boot); static struct attribute *icnss_attrs[] = { &dev_attr_qdss_tr_start.attr, &dev_attr_qdss_tr_stop.attr, &dev_attr_qdss_conf_download.attr, &dev_attr_hw_trc_override.attr, &dev_attr_wpss_boot.attr, NULL, }; static struct attribute_group icnss_attr_group = { .attrs = icnss_attrs, }; static int icnss_create_sysfs_link(struct icnss_priv *priv) { struct device *dev = &priv->pdev->dev; int ret; ret = sysfs_create_link(kernel_kobj, &dev->kobj, "icnss"); if (ret) { icnss_pr_err("Failed to create icnss link, err = %d\n", ret); goto out; } return 0; out: return ret; } static void icnss_remove_sysfs_link(struct icnss_priv *priv) { sysfs_remove_link(kernel_kobj, "icnss"); } static int icnss_sysfs_create(struct icnss_priv *priv) { int ret = 0; ret = devm_device_add_group(&priv->pdev->dev, &icnss_attr_group); if (ret) { icnss_pr_err("Failed to create icnss device group, err = %d\n", ret); goto out; } icnss_create_sysfs_link(priv); ret = icnss_create_shutdown_sysfs(priv); if (ret) goto remove_icnss_group; return 0; remove_icnss_group: devm_device_remove_group(&priv->pdev->dev, &icnss_attr_group); out: return ret; } static void icnss_sysfs_destroy(struct icnss_priv *priv) { icnss_destroy_shutdown_sysfs(priv); icnss_remove_sysfs_link(priv); devm_device_remove_group(&priv->pdev->dev, &icnss_attr_group); } static int icnss_get_vbatt_info(struct icnss_priv *priv) { struct adc_tm_chip *adc_tm_dev = NULL; Loading Loading @@ -3488,6 +3687,7 @@ static int icnss_probe(struct platform_device *pdev) icnss_get_cpr_info(priv); icnss_get_smp2p_info(priv); set_bit(ICNSS_COLD_BOOT_CAL, &priv->state); INIT_WORK(&wpss_loader, icnss_wpss_load); } INIT_LIST_HEAD(&priv->icnss_tcdev_list); Loading drivers/soc/qcom/icnss2/main.h +5 −1 Original line number Diff line number Diff line /* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. * Copyright (c) 2017-2020, 2021, The Linux Foundation. * All rights reserved. */ #ifndef __MAIN_H__ Loading Loading @@ -57,6 +58,7 @@ enum icnss_driver_event_type { ICNSS_DRIVER_EVENT_QDSS_TRACE_SAVE, ICNSS_DRIVER_EVENT_QDSS_TRACE_FREE, ICNSS_DRIVER_EVENT_M3_DUMP_UPLOAD_REQ, ICNSS_DRIVER_EVENT_QDSS_TRACE_REQ_DATA, ICNSS_DRIVER_EVENT_MAX, }; Loading Loading @@ -418,6 +420,7 @@ struct icnss_priv { bool is_ssr; bool smmu_s1_enable; struct kobject *icnss_kobject; void *subsys; atomic_t is_shutdown; u32 qdss_mem_seg_len; struct icnss_fw_mem qdss_mem[QMI_WLFW_MAX_NUM_MEM_SEG]; Loading @@ -432,6 +435,7 @@ struct icnss_priv { struct mutex tcdev_lock; bool is_chain1_supported; bool chain_reg_info_updated; u32 hw_trc_override; }; struct icnss_reg_info { Loading drivers/soc/qcom/icnss2/qmi.c +335 −7 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ #include "main.h" #include "qmi.h" #include "debug.h" #include "genl.h" #define WLFW_SERVICE_WCN_INS_ID_V01 3 #define WLFW_SERVICE_INS_ID_V01 0 Loading @@ -42,6 +43,8 @@ #define REGDB_FILE_NAME "regdb.bin" #define DUMMY_BDF_FILE_NAME "bdwlan.dmy" #define QDSS_TRACE_CONFIG_FILE "qdss_trace_config.cfg" #define DEVICE_BAR_SIZE 0x200000 #define M3_SEGMENT_ADDR_MASK 0xFFFFFFFF Loading Loading @@ -891,6 +894,238 @@ int icnss_wlfw_bdf_dnld_send_sync(struct icnss_priv *priv, u32 bdf_type) return ret; } int icnss_wlfw_qdss_data_send_sync(struct icnss_priv *priv, char *file_name, u32 total_size) { int ret = 0; struct wlfw_qdss_trace_data_req_msg_v01 *req; struct wlfw_qdss_trace_data_resp_msg_v01 *resp; unsigned char *p_qdss_trace_data_temp, *p_qdss_trace_data = NULL; unsigned int remaining; struct qmi_txn txn; icnss_pr_dbg("%s", __func__); req = kzalloc(sizeof(*req), GFP_KERNEL); if (!req) return -ENOMEM; resp = kzalloc(sizeof(*resp), GFP_KERNEL); if (!resp) { kfree(req); return -ENOMEM; } p_qdss_trace_data = kzalloc(total_size, GFP_KERNEL); if (!p_qdss_trace_data) { ret = ENOMEM; goto end; } remaining = total_size; p_qdss_trace_data_temp = p_qdss_trace_data; while (remaining && resp->end == 0) { ret = qmi_txn_init(&priv->qmi, &txn, wlfw_qdss_trace_data_resp_msg_v01_ei, resp); if (ret < 0) { icnss_pr_err("Fail to init txn for QDSS trace resp %d\n", ret); goto fail; } ret = qmi_send_request (&priv->qmi, NULL, &txn, QMI_WLFW_QDSS_TRACE_DATA_REQ_V01, WLFW_QDSS_TRACE_DATA_REQ_MSG_V01_MAX_MSG_LEN, wlfw_qdss_trace_data_req_msg_v01_ei, req); if (ret < 0) { qmi_txn_cancel(&txn); icnss_pr_err("Fail to send QDSS trace data req %d\n", ret); goto fail; } ret = qmi_txn_wait(&txn, priv->ctrl_params.qmi_timeout); if (ret < 0) { icnss_pr_err("QDSS trace resp wait failed with rc %d\n", ret); goto fail; } else if (resp->resp.result != QMI_RESULT_SUCCESS_V01) { icnss_pr_err("QMI QDSS trace request rejected, result:%d error:%d\n", resp->resp.result, resp->resp.error); ret = -resp->resp.result; goto fail; } else { ret = 0; } icnss_pr_dbg("%s: response total size %d data len %d", __func__, resp->total_size, resp->data_len); if ((resp->total_size_valid == 1 && resp->total_size == total_size) && (resp->seg_id_valid == 1 && resp->seg_id == req->seg_id) && (resp->data_valid == 1 && resp->data_len <= QMI_WLFW_MAX_DATA_SIZE_V01)) { memcpy(p_qdss_trace_data_temp, resp->data, resp->data_len); } else { icnss_pr_err("%s: Unmatched qdss trace data, Expect total_size %u, seg_id %u, Recv total_size_valid %u, total_size %u, seg_id_valid %u, seg_id %u, data_len_valid %u, data_len %u", __func__, total_size, req->seg_id, resp->total_size_valid, resp->total_size, resp->seg_id_valid, resp->seg_id, resp->data_valid, resp->data_len); ret = -EINVAL; goto fail; } remaining -= resp->data_len; p_qdss_trace_data_temp += resp->data_len; req->seg_id++; } if (remaining == 0 && (resp->end_valid && resp->end)) { ret = icnss_genl_send_msg(p_qdss_trace_data, ICNSS_GENL_MSG_TYPE_QDSS, file_name, total_size); if (ret < 0) { icnss_pr_err("Fail to save QDSS trace data: %d\n", ret); ret = -EINVAL; } } else { icnss_pr_err("%s: QDSS trace file corrupted: remaining %u, end_valid %u, end %u", __func__, remaining, resp->end_valid, resp->end); ret = -EINVAL; } fail: kfree(p_qdss_trace_data); end: kfree(req); kfree(resp); return ret; } int icnss_wlfw_qdss_dnld_send_sync(struct icnss_priv *priv) { struct wlfw_qdss_trace_config_download_req_msg_v01 *req; struct wlfw_qdss_trace_config_download_resp_msg_v01 *resp; struct qmi_txn txn; char filename[ICNSS_MAX_FILE_NAME]; const struct firmware *fw_entry = NULL; const u8 *temp; unsigned int remaining; int ret = 0; icnss_pr_dbg("Sending QDSS config download message, state: 0x%lx\n", priv->state); req = kzalloc(sizeof(*req), GFP_KERNEL); if (!req) return -ENOMEM; resp = kzalloc(sizeof(*resp), GFP_KERNEL); if (!resp) { kfree(req); return -ENOMEM; } icnss_add_fw_prefix_name(priv, filename, QDSS_TRACE_CONFIG_FILE); ret = request_firmware(&fw_entry, filename, &priv->pdev->dev); if (ret) { icnss_pr_err("Failed to load QDSS: %s\n", filename); goto err_req_fw; } temp = fw_entry->data; remaining = fw_entry->size; icnss_pr_dbg("Downloading QDSS: %s, size: %u\n", filename, remaining); while (remaining) { req->total_size_valid = 1; req->total_size = remaining; req->seg_id_valid = 1; req->data_valid = 1; req->end_valid = 1; if (remaining > QMI_WLFW_MAX_DATA_SIZE_V01) { req->data_len = QMI_WLFW_MAX_DATA_SIZE_V01; } else { req->data_len = remaining; req->end = 1; } memcpy(req->data, temp, req->data_len); ret = qmi_txn_init (&priv->qmi, &txn, wlfw_qdss_trace_config_download_resp_msg_v01_ei, resp); if (ret < 0) { icnss_pr_err("Failed to initialize txn for QDSS download request, err: %d\n", ret); goto err_send; } ret = qmi_send_request (&priv->qmi, NULL, &txn, QMI_WLFW_QDSS_TRACE_CONFIG_DOWNLOAD_REQ_V01, WLFW_QDSS_TRACE_CONFIG_DOWNLOAD_REQ_MSG_V01_MAX_MSG_LEN, wlfw_qdss_trace_config_download_req_msg_v01_ei, req); if (ret < 0) { qmi_txn_cancel(&txn); icnss_pr_err("Failed to send respond QDSS download request, err: %d\n", ret); goto err_send; } ret = qmi_txn_wait(&txn, priv->ctrl_params.qmi_timeout); if (ret < 0) { icnss_pr_err("Failed to wait for response of QDSS download request, err: %d\n", ret); goto err_send; } if (resp->resp.result != QMI_RESULT_SUCCESS_V01) { icnss_pr_err("QDSS download request failed, result: %d, err: %d\n", resp->resp.result, resp->resp.error); ret = -resp->resp.result; goto err_send; } remaining -= req->data_len; temp += req->data_len; req->seg_id++; } release_firmware(fw_entry); kfree(req); kfree(resp); return 0; err_send: release_firmware(fw_entry); err_req_fw: kfree(req); kfree(resp); return ret; } int wlfw_wlan_mode_send_sync_msg(struct icnss_priv *priv, enum wlfw_driver_mode_enum_v01 mode) { Loading Loading @@ -980,6 +1215,94 @@ int wlfw_wlan_mode_send_sync_msg(struct icnss_priv *priv, return ret; } static int wlfw_send_qdss_trace_mode_req (struct icnss_priv *priv, enum wlfw_qdss_trace_mode_enum_v01 mode, unsigned long long option) { int rc = 0; int tmp = 0; struct wlfw_qdss_trace_mode_req_msg_v01 *req; struct wlfw_qdss_trace_mode_resp_msg_v01 *resp; struct qmi_txn txn; if (!priv) return -ENODEV; req = kzalloc(sizeof(*req), GFP_KERNEL); if (!req) return -ENOMEM; resp = kzalloc(sizeof(*resp), GFP_KERNEL); if (!resp) { kfree(req); return -ENOMEM; } req->mode_valid = 1; req->mode = mode; req->option_valid = 1; req->option = option; tmp = priv->hw_trc_override; req->hw_trc_disable_override_valid = 1; req->hw_trc_disable_override = (tmp > QMI_PARAM_DISABLE_V01 ? QMI_PARAM_DISABLE_V01 : (tmp < 0 ? QMI_PARAM_INVALID_V01 : tmp)); icnss_pr_dbg("%s: mode %u, option %llu, hw_trc_disable_override: %u", __func__, mode, option, req->hw_trc_disable_override); rc = qmi_txn_init(&priv->qmi, &txn, wlfw_qdss_trace_mode_resp_msg_v01_ei, resp); if (rc < 0) { icnss_qmi_fatal_err("Fail to init txn for QDSS Mode resp %d\n", rc); goto out; } rc = qmi_send_request(&priv->qmi, NULL, &txn, QMI_WLFW_QDSS_TRACE_MODE_REQ_V01, WLFW_QDSS_TRACE_MODE_REQ_MSG_V01_MAX_MSG_LEN, wlfw_qdss_trace_mode_req_msg_v01_ei, req); if (rc < 0) { qmi_txn_cancel(&txn); icnss_qmi_fatal_err("Fail to send QDSS Mode req %d\n", rc); goto out; } rc = qmi_txn_wait(&txn, priv->ctrl_params.qmi_timeout); if (rc < 0) { icnss_qmi_fatal_err("QDSS Mode resp wait failed with rc %d\n", rc); goto out; } else if (resp->resp.result != QMI_RESULT_SUCCESS_V01) { icnss_qmi_fatal_err( "QMI QDSS Mode request rejected, result:%d error:%d\n", resp->resp.result, resp->resp.error); rc = -resp->resp.result; goto out; } out: kfree(resp); kfree(req); return rc; } int wlfw_qdss_trace_start(struct icnss_priv *priv) { return wlfw_send_qdss_trace_mode_req(priv, QMI_WLFW_QDSS_TRACE_ON_V01, 0); } int wlfw_qdss_trace_stop(struct icnss_priv *priv, unsigned long long option) { return wlfw_send_qdss_trace_mode_req(priv, QMI_WLFW_QDSS_TRACE_OFF_V01, option); } int wlfw_wlan_cfg_send_sync_msg(struct icnss_priv *priv, struct wlfw_wlan_cfg_req_msg_v01 *data) { Loading Loading @@ -1951,9 +2274,6 @@ static void wlfw_qdss_trace_save_ind_cb(struct qmi_handle *qmi, ind_msg->source, ind_msg->total_size, ind_msg->file_name_valid, ind_msg->file_name); if (ind_msg->source == 1) return; event_data = kzalloc(sizeof(*event_data), GFP_KERNEL); if (!event_data) return; Loading Loading @@ -1981,12 +2301,20 @@ static void wlfw_qdss_trace_save_ind_cb(struct qmi_handle *qmi, if (ind_msg->file_name_valid) strlcpy(event_data->file_name, ind_msg->file_name, QDSS_TRACE_FILE_NAME_MAX + 1); else strlcpy(event_data->file_name, "qdss_trace", QDSS_TRACE_FILE_NAME_MAX + 1); if (ind_msg->source == 1) { if (!ind_msg->file_name_valid) strlcpy(event_data->file_name, "qdss_trace_wcss_etb", QDSS_TRACE_FILE_NAME_MAX + 1); icnss_driver_event_post(priv, ICNSS_DRIVER_EVENT_QDSS_TRACE_REQ_DATA, 0, event_data); } else { if (!ind_msg->file_name_valid) strlcpy(event_data->file_name, "qdss_trace_ddr", QDSS_TRACE_FILE_NAME_MAX + 1); icnss_driver_event_post(priv, ICNSS_DRIVER_EVENT_QDSS_TRACE_SAVE, 0, event_data); } return; Loading drivers/soc/qcom/icnss2/qmi.h +10 −0 Original line number Diff line number Diff line Loading @@ -36,6 +36,11 @@ struct icnss_m3_upload_segments_req_data { struct icnss_m3_segment m3_segment[M3_SEGMENTS_SIZE_MAX]; }; struct icnss_qmi_event_qdss_trace_req_data { u32 total_size; char file_name[QDSS_TRACE_FILE_NAME_MAX + 1]; }; #ifndef CONFIG_ICNSS2_QMI static inline int wlfw_ind_register_send_sync_msg(struct icnss_priv *priv) Loading Loading @@ -201,6 +206,11 @@ int wlfw_device_info_send_msg(struct icnss_priv *priv); int wlfw_wlan_mode_send_sync_msg(struct icnss_priv *priv, enum wlfw_driver_mode_enum_v01 mode); int icnss_wlfw_bdf_dnld_send_sync(struct icnss_priv *priv, u32 bdf_type); int icnss_wlfw_qdss_dnld_send_sync(struct icnss_priv *priv); int icnss_wlfw_qdss_data_send_sync(struct icnss_priv *priv, char *file_name, u32 total_size); int wlfw_qdss_trace_start(struct icnss_priv *priv); int wlfw_qdss_trace_stop(struct icnss_priv *priv, unsigned long long option); int wlfw_qdss_trace_mem_info_send_sync(struct icnss_priv *priv); int wlfw_power_save_send_msg(struct icnss_priv *priv, enum wlfw_power_save_mode_v01 mode); Loading Loading
drivers/soc/qcom/icnss2/main.c +210 −10 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2015-2020, The Linux Foundation. All rights reserved. * Copyright (c) 2015-2020, 2021, The Linux Foundation. * All rights reserved. */ #define pr_fmt(fmt) "icnss2: " fmt Loading Loading @@ -74,7 +75,7 @@ #define WLFW_TIMEOUT msecs_to_jiffies(3000) static struct icnss_priv *penv; static struct work_struct wpss_loader; uint64_t dynamic_feature_mask = ICNSS_DEFAULT_FEATURE_MASK; #define ICNSS_EVENT_PENDING 2989 Loading Loading @@ -183,6 +184,8 @@ char *icnss_driver_event_to_str(enum icnss_driver_event_type type) return "QDSS_TRACE_FREE"; case ICNSS_DRIVER_EVENT_M3_DUMP_UPLOAD_REQ: return "M3_DUMP_UPLOAD"; case ICNSS_DRIVER_EVENT_QDSS_TRACE_REQ_DATA: return "QDSS_TRACE_REQ_DATA"; case ICNSS_DRIVER_EVENT_MAX: return "EVENT_MAX"; } Loading Loading @@ -833,6 +836,9 @@ static int icnss_driver_event_fw_init_done(struct icnss_priv *priv, void *data) icnss_pr_info("WLAN FW Initialization done: 0x%lx\n", priv->state); if (icnss_wlfw_qdss_dnld_send_sync(priv)) icnss_pr_info("Failed to download qdss configuration file"); if (test_bit(ICNSS_COLD_BOOT_CAL, &priv->state)) ret = wlfw_wlan_mode_send_sync_msg(priv, (enum wlfw_driver_mode_enum_v01)ICNSS_CALIBRATION); Loading Loading @@ -1008,6 +1014,25 @@ static inline int icnss_atomic_dec_if_greater_one(atomic_t *v) return dec; } static int icnss_qdss_trace_req_data_hdlr(struct icnss_priv *priv, void *data) { int ret = 0; struct icnss_qmi_event_qdss_trace_save_data *event_data = data; if (!priv) return -ENODEV; if (!data) return -EINVAL; ret = icnss_wlfw_qdss_data_send_sync(priv, event_data->file_name, event_data->total_size); kfree(data); return ret; } static int icnss_event_soc_wake_request(struct icnss_priv *priv, void *data) { int ret = 0; Loading Loading @@ -1432,6 +1457,9 @@ static void icnss_driver_event_work(struct work_struct *work) break; case ICNSS_DRIVER_EVENT_M3_DUMP_UPLOAD_REQ: ret = icnss_m3_dump_upload_req_hdlr(priv, event->data); case ICNSS_DRIVER_EVENT_QDSS_TRACE_REQ_DATA: ret = icnss_qdss_trace_req_data_hdlr(priv, event->data); break; default: icnss_pr_err("Invalid Event type: %d", event->type); Loading Loading @@ -2968,27 +2996,31 @@ void icnss_disallow_recursive_recovery(struct device *dev) icnss_pr_info("Recursive recovery disallowed for WLAN\n"); } static void icnss_sysfs_create(struct icnss_priv *priv) static int icnss_create_shutdown_sysfs(struct icnss_priv *priv) { struct kobject *icnss_kobject; int error = 0; int ret = 0; atomic_set(&priv->is_shutdown, false); icnss_kobject = kobject_create_and_add("shutdown_wlan", kernel_kobj); if (!icnss_kobject) { icnss_pr_err("Unable to create kernel object"); return; icnss_pr_err("Unable to create shutdown_wlan kernel object"); return -EINVAL; } priv->icnss_kobject = icnss_kobject; error = sysfs_create_file(icnss_kobject, &icnss_sysfs_attribute.attr); if (error) icnss_pr_err("Unable to create icnss sysfs file"); ret = sysfs_create_file(icnss_kobject, &icnss_sysfs_attribute.attr); if (ret) { icnss_pr_err("Unable to create icnss sysfs file err:%d", ret); return ret; } static void icnss_sysfs_destroy(struct icnss_priv *priv) return ret; } static void icnss_destroy_shutdown_sysfs(struct icnss_priv *priv) { struct kobject *icnss_kobject; Loading @@ -2997,6 +3029,173 @@ static void icnss_sysfs_destroy(struct icnss_priv *priv) kobject_put(icnss_kobject); } static ssize_t qdss_tr_start_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct icnss_priv *priv = dev_get_drvdata(dev); wlfw_qdss_trace_start(priv); icnss_pr_dbg("Received QDSS start command\n"); return count; } static ssize_t qdss_tr_stop_store(struct device *dev, struct device_attribute *attr, const char *user_buf, size_t count) { struct icnss_priv *priv = dev_get_drvdata(dev); u32 option = 0; if (sscanf(user_buf, "%du", &option) != 1) return -EINVAL; wlfw_qdss_trace_stop(priv, option); icnss_pr_dbg("Received QDSS stop command\n"); return count; } static ssize_t qdss_conf_download_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct icnss_priv *priv = dev_get_drvdata(dev); icnss_wlfw_qdss_dnld_send_sync(priv); icnss_pr_dbg("Received QDSS download config command\n"); return count; } static ssize_t hw_trc_override_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct icnss_priv *priv = dev_get_drvdata(dev); int tmp = 0; if (sscanf(buf, "%du", &tmp) != 1) return -EINVAL; priv->hw_trc_override = tmp; icnss_pr_dbg("Received QDSS hw_trc_override indication\n"); return count; } static void icnss_wpss_load(struct work_struct *wpss_load_work) { struct icnss_priv *priv = icnss_get_plat_priv(); priv->subsys = subsystem_get("wpss"); if (IS_ERR(priv->subsys)) icnss_pr_err("Failed to load wpss subsys"); } static inline void icnss_wpss_unload(struct icnss_priv *priv) { if (priv->subsys) { subsystem_put(priv->subsys); priv->subsys = NULL; } } static ssize_t wpss_boot_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct icnss_priv *priv = dev_get_drvdata(dev); int wpss_subsys = 0; if (priv->device_id != WCN6750_DEVICE_ID) return count; if (sscanf(buf, "%du", &wpss_subsys) != 1) { icnss_pr_err("Failed to read wpss_subsys info"); return -EINVAL; } icnss_pr_dbg("WPSS Subsystem: %s", wpss_subsys ? "GET" : "PUT"); if (wpss_subsys == 1) schedule_work(&wpss_loader); else if (wpss_subsys == 0) icnss_wpss_unload(priv); return count; } static DEVICE_ATTR_WO(qdss_tr_start); static DEVICE_ATTR_WO(qdss_tr_stop); static DEVICE_ATTR_WO(qdss_conf_download); static DEVICE_ATTR_WO(hw_trc_override); static DEVICE_ATTR_WO(wpss_boot); static struct attribute *icnss_attrs[] = { &dev_attr_qdss_tr_start.attr, &dev_attr_qdss_tr_stop.attr, &dev_attr_qdss_conf_download.attr, &dev_attr_hw_trc_override.attr, &dev_attr_wpss_boot.attr, NULL, }; static struct attribute_group icnss_attr_group = { .attrs = icnss_attrs, }; static int icnss_create_sysfs_link(struct icnss_priv *priv) { struct device *dev = &priv->pdev->dev; int ret; ret = sysfs_create_link(kernel_kobj, &dev->kobj, "icnss"); if (ret) { icnss_pr_err("Failed to create icnss link, err = %d\n", ret); goto out; } return 0; out: return ret; } static void icnss_remove_sysfs_link(struct icnss_priv *priv) { sysfs_remove_link(kernel_kobj, "icnss"); } static int icnss_sysfs_create(struct icnss_priv *priv) { int ret = 0; ret = devm_device_add_group(&priv->pdev->dev, &icnss_attr_group); if (ret) { icnss_pr_err("Failed to create icnss device group, err = %d\n", ret); goto out; } icnss_create_sysfs_link(priv); ret = icnss_create_shutdown_sysfs(priv); if (ret) goto remove_icnss_group; return 0; remove_icnss_group: devm_device_remove_group(&priv->pdev->dev, &icnss_attr_group); out: return ret; } static void icnss_sysfs_destroy(struct icnss_priv *priv) { icnss_destroy_shutdown_sysfs(priv); icnss_remove_sysfs_link(priv); devm_device_remove_group(&priv->pdev->dev, &icnss_attr_group); } static int icnss_get_vbatt_info(struct icnss_priv *priv) { struct adc_tm_chip *adc_tm_dev = NULL; Loading Loading @@ -3488,6 +3687,7 @@ static int icnss_probe(struct platform_device *pdev) icnss_get_cpr_info(priv); icnss_get_smp2p_info(priv); set_bit(ICNSS_COLD_BOOT_CAL, &priv->state); INIT_WORK(&wpss_loader, icnss_wpss_load); } INIT_LIST_HEAD(&priv->icnss_tcdev_list); Loading
drivers/soc/qcom/icnss2/main.h +5 −1 Original line number Diff line number Diff line /* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. * Copyright (c) 2017-2020, 2021, The Linux Foundation. * All rights reserved. */ #ifndef __MAIN_H__ Loading Loading @@ -57,6 +58,7 @@ enum icnss_driver_event_type { ICNSS_DRIVER_EVENT_QDSS_TRACE_SAVE, ICNSS_DRIVER_EVENT_QDSS_TRACE_FREE, ICNSS_DRIVER_EVENT_M3_DUMP_UPLOAD_REQ, ICNSS_DRIVER_EVENT_QDSS_TRACE_REQ_DATA, ICNSS_DRIVER_EVENT_MAX, }; Loading Loading @@ -418,6 +420,7 @@ struct icnss_priv { bool is_ssr; bool smmu_s1_enable; struct kobject *icnss_kobject; void *subsys; atomic_t is_shutdown; u32 qdss_mem_seg_len; struct icnss_fw_mem qdss_mem[QMI_WLFW_MAX_NUM_MEM_SEG]; Loading @@ -432,6 +435,7 @@ struct icnss_priv { struct mutex tcdev_lock; bool is_chain1_supported; bool chain_reg_info_updated; u32 hw_trc_override; }; struct icnss_reg_info { Loading
drivers/soc/qcom/icnss2/qmi.c +335 −7 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ #include "main.h" #include "qmi.h" #include "debug.h" #include "genl.h" #define WLFW_SERVICE_WCN_INS_ID_V01 3 #define WLFW_SERVICE_INS_ID_V01 0 Loading @@ -42,6 +43,8 @@ #define REGDB_FILE_NAME "regdb.bin" #define DUMMY_BDF_FILE_NAME "bdwlan.dmy" #define QDSS_TRACE_CONFIG_FILE "qdss_trace_config.cfg" #define DEVICE_BAR_SIZE 0x200000 #define M3_SEGMENT_ADDR_MASK 0xFFFFFFFF Loading Loading @@ -891,6 +894,238 @@ int icnss_wlfw_bdf_dnld_send_sync(struct icnss_priv *priv, u32 bdf_type) return ret; } int icnss_wlfw_qdss_data_send_sync(struct icnss_priv *priv, char *file_name, u32 total_size) { int ret = 0; struct wlfw_qdss_trace_data_req_msg_v01 *req; struct wlfw_qdss_trace_data_resp_msg_v01 *resp; unsigned char *p_qdss_trace_data_temp, *p_qdss_trace_data = NULL; unsigned int remaining; struct qmi_txn txn; icnss_pr_dbg("%s", __func__); req = kzalloc(sizeof(*req), GFP_KERNEL); if (!req) return -ENOMEM; resp = kzalloc(sizeof(*resp), GFP_KERNEL); if (!resp) { kfree(req); return -ENOMEM; } p_qdss_trace_data = kzalloc(total_size, GFP_KERNEL); if (!p_qdss_trace_data) { ret = ENOMEM; goto end; } remaining = total_size; p_qdss_trace_data_temp = p_qdss_trace_data; while (remaining && resp->end == 0) { ret = qmi_txn_init(&priv->qmi, &txn, wlfw_qdss_trace_data_resp_msg_v01_ei, resp); if (ret < 0) { icnss_pr_err("Fail to init txn for QDSS trace resp %d\n", ret); goto fail; } ret = qmi_send_request (&priv->qmi, NULL, &txn, QMI_WLFW_QDSS_TRACE_DATA_REQ_V01, WLFW_QDSS_TRACE_DATA_REQ_MSG_V01_MAX_MSG_LEN, wlfw_qdss_trace_data_req_msg_v01_ei, req); if (ret < 0) { qmi_txn_cancel(&txn); icnss_pr_err("Fail to send QDSS trace data req %d\n", ret); goto fail; } ret = qmi_txn_wait(&txn, priv->ctrl_params.qmi_timeout); if (ret < 0) { icnss_pr_err("QDSS trace resp wait failed with rc %d\n", ret); goto fail; } else if (resp->resp.result != QMI_RESULT_SUCCESS_V01) { icnss_pr_err("QMI QDSS trace request rejected, result:%d error:%d\n", resp->resp.result, resp->resp.error); ret = -resp->resp.result; goto fail; } else { ret = 0; } icnss_pr_dbg("%s: response total size %d data len %d", __func__, resp->total_size, resp->data_len); if ((resp->total_size_valid == 1 && resp->total_size == total_size) && (resp->seg_id_valid == 1 && resp->seg_id == req->seg_id) && (resp->data_valid == 1 && resp->data_len <= QMI_WLFW_MAX_DATA_SIZE_V01)) { memcpy(p_qdss_trace_data_temp, resp->data, resp->data_len); } else { icnss_pr_err("%s: Unmatched qdss trace data, Expect total_size %u, seg_id %u, Recv total_size_valid %u, total_size %u, seg_id_valid %u, seg_id %u, data_len_valid %u, data_len %u", __func__, total_size, req->seg_id, resp->total_size_valid, resp->total_size, resp->seg_id_valid, resp->seg_id, resp->data_valid, resp->data_len); ret = -EINVAL; goto fail; } remaining -= resp->data_len; p_qdss_trace_data_temp += resp->data_len; req->seg_id++; } if (remaining == 0 && (resp->end_valid && resp->end)) { ret = icnss_genl_send_msg(p_qdss_trace_data, ICNSS_GENL_MSG_TYPE_QDSS, file_name, total_size); if (ret < 0) { icnss_pr_err("Fail to save QDSS trace data: %d\n", ret); ret = -EINVAL; } } else { icnss_pr_err("%s: QDSS trace file corrupted: remaining %u, end_valid %u, end %u", __func__, remaining, resp->end_valid, resp->end); ret = -EINVAL; } fail: kfree(p_qdss_trace_data); end: kfree(req); kfree(resp); return ret; } int icnss_wlfw_qdss_dnld_send_sync(struct icnss_priv *priv) { struct wlfw_qdss_trace_config_download_req_msg_v01 *req; struct wlfw_qdss_trace_config_download_resp_msg_v01 *resp; struct qmi_txn txn; char filename[ICNSS_MAX_FILE_NAME]; const struct firmware *fw_entry = NULL; const u8 *temp; unsigned int remaining; int ret = 0; icnss_pr_dbg("Sending QDSS config download message, state: 0x%lx\n", priv->state); req = kzalloc(sizeof(*req), GFP_KERNEL); if (!req) return -ENOMEM; resp = kzalloc(sizeof(*resp), GFP_KERNEL); if (!resp) { kfree(req); return -ENOMEM; } icnss_add_fw_prefix_name(priv, filename, QDSS_TRACE_CONFIG_FILE); ret = request_firmware(&fw_entry, filename, &priv->pdev->dev); if (ret) { icnss_pr_err("Failed to load QDSS: %s\n", filename); goto err_req_fw; } temp = fw_entry->data; remaining = fw_entry->size; icnss_pr_dbg("Downloading QDSS: %s, size: %u\n", filename, remaining); while (remaining) { req->total_size_valid = 1; req->total_size = remaining; req->seg_id_valid = 1; req->data_valid = 1; req->end_valid = 1; if (remaining > QMI_WLFW_MAX_DATA_SIZE_V01) { req->data_len = QMI_WLFW_MAX_DATA_SIZE_V01; } else { req->data_len = remaining; req->end = 1; } memcpy(req->data, temp, req->data_len); ret = qmi_txn_init (&priv->qmi, &txn, wlfw_qdss_trace_config_download_resp_msg_v01_ei, resp); if (ret < 0) { icnss_pr_err("Failed to initialize txn for QDSS download request, err: %d\n", ret); goto err_send; } ret = qmi_send_request (&priv->qmi, NULL, &txn, QMI_WLFW_QDSS_TRACE_CONFIG_DOWNLOAD_REQ_V01, WLFW_QDSS_TRACE_CONFIG_DOWNLOAD_REQ_MSG_V01_MAX_MSG_LEN, wlfw_qdss_trace_config_download_req_msg_v01_ei, req); if (ret < 0) { qmi_txn_cancel(&txn); icnss_pr_err("Failed to send respond QDSS download request, err: %d\n", ret); goto err_send; } ret = qmi_txn_wait(&txn, priv->ctrl_params.qmi_timeout); if (ret < 0) { icnss_pr_err("Failed to wait for response of QDSS download request, err: %d\n", ret); goto err_send; } if (resp->resp.result != QMI_RESULT_SUCCESS_V01) { icnss_pr_err("QDSS download request failed, result: %d, err: %d\n", resp->resp.result, resp->resp.error); ret = -resp->resp.result; goto err_send; } remaining -= req->data_len; temp += req->data_len; req->seg_id++; } release_firmware(fw_entry); kfree(req); kfree(resp); return 0; err_send: release_firmware(fw_entry); err_req_fw: kfree(req); kfree(resp); return ret; } int wlfw_wlan_mode_send_sync_msg(struct icnss_priv *priv, enum wlfw_driver_mode_enum_v01 mode) { Loading Loading @@ -980,6 +1215,94 @@ int wlfw_wlan_mode_send_sync_msg(struct icnss_priv *priv, return ret; } static int wlfw_send_qdss_trace_mode_req (struct icnss_priv *priv, enum wlfw_qdss_trace_mode_enum_v01 mode, unsigned long long option) { int rc = 0; int tmp = 0; struct wlfw_qdss_trace_mode_req_msg_v01 *req; struct wlfw_qdss_trace_mode_resp_msg_v01 *resp; struct qmi_txn txn; if (!priv) return -ENODEV; req = kzalloc(sizeof(*req), GFP_KERNEL); if (!req) return -ENOMEM; resp = kzalloc(sizeof(*resp), GFP_KERNEL); if (!resp) { kfree(req); return -ENOMEM; } req->mode_valid = 1; req->mode = mode; req->option_valid = 1; req->option = option; tmp = priv->hw_trc_override; req->hw_trc_disable_override_valid = 1; req->hw_trc_disable_override = (tmp > QMI_PARAM_DISABLE_V01 ? QMI_PARAM_DISABLE_V01 : (tmp < 0 ? QMI_PARAM_INVALID_V01 : tmp)); icnss_pr_dbg("%s: mode %u, option %llu, hw_trc_disable_override: %u", __func__, mode, option, req->hw_trc_disable_override); rc = qmi_txn_init(&priv->qmi, &txn, wlfw_qdss_trace_mode_resp_msg_v01_ei, resp); if (rc < 0) { icnss_qmi_fatal_err("Fail to init txn for QDSS Mode resp %d\n", rc); goto out; } rc = qmi_send_request(&priv->qmi, NULL, &txn, QMI_WLFW_QDSS_TRACE_MODE_REQ_V01, WLFW_QDSS_TRACE_MODE_REQ_MSG_V01_MAX_MSG_LEN, wlfw_qdss_trace_mode_req_msg_v01_ei, req); if (rc < 0) { qmi_txn_cancel(&txn); icnss_qmi_fatal_err("Fail to send QDSS Mode req %d\n", rc); goto out; } rc = qmi_txn_wait(&txn, priv->ctrl_params.qmi_timeout); if (rc < 0) { icnss_qmi_fatal_err("QDSS Mode resp wait failed with rc %d\n", rc); goto out; } else if (resp->resp.result != QMI_RESULT_SUCCESS_V01) { icnss_qmi_fatal_err( "QMI QDSS Mode request rejected, result:%d error:%d\n", resp->resp.result, resp->resp.error); rc = -resp->resp.result; goto out; } out: kfree(resp); kfree(req); return rc; } int wlfw_qdss_trace_start(struct icnss_priv *priv) { return wlfw_send_qdss_trace_mode_req(priv, QMI_WLFW_QDSS_TRACE_ON_V01, 0); } int wlfw_qdss_trace_stop(struct icnss_priv *priv, unsigned long long option) { return wlfw_send_qdss_trace_mode_req(priv, QMI_WLFW_QDSS_TRACE_OFF_V01, option); } int wlfw_wlan_cfg_send_sync_msg(struct icnss_priv *priv, struct wlfw_wlan_cfg_req_msg_v01 *data) { Loading Loading @@ -1951,9 +2274,6 @@ static void wlfw_qdss_trace_save_ind_cb(struct qmi_handle *qmi, ind_msg->source, ind_msg->total_size, ind_msg->file_name_valid, ind_msg->file_name); if (ind_msg->source == 1) return; event_data = kzalloc(sizeof(*event_data), GFP_KERNEL); if (!event_data) return; Loading Loading @@ -1981,12 +2301,20 @@ static void wlfw_qdss_trace_save_ind_cb(struct qmi_handle *qmi, if (ind_msg->file_name_valid) strlcpy(event_data->file_name, ind_msg->file_name, QDSS_TRACE_FILE_NAME_MAX + 1); else strlcpy(event_data->file_name, "qdss_trace", QDSS_TRACE_FILE_NAME_MAX + 1); if (ind_msg->source == 1) { if (!ind_msg->file_name_valid) strlcpy(event_data->file_name, "qdss_trace_wcss_etb", QDSS_TRACE_FILE_NAME_MAX + 1); icnss_driver_event_post(priv, ICNSS_DRIVER_EVENT_QDSS_TRACE_REQ_DATA, 0, event_data); } else { if (!ind_msg->file_name_valid) strlcpy(event_data->file_name, "qdss_trace_ddr", QDSS_TRACE_FILE_NAME_MAX + 1); icnss_driver_event_post(priv, ICNSS_DRIVER_EVENT_QDSS_TRACE_SAVE, 0, event_data); } return; Loading
drivers/soc/qcom/icnss2/qmi.h +10 −0 Original line number Diff line number Diff line Loading @@ -36,6 +36,11 @@ struct icnss_m3_upload_segments_req_data { struct icnss_m3_segment m3_segment[M3_SEGMENTS_SIZE_MAX]; }; struct icnss_qmi_event_qdss_trace_req_data { u32 total_size; char file_name[QDSS_TRACE_FILE_NAME_MAX + 1]; }; #ifndef CONFIG_ICNSS2_QMI static inline int wlfw_ind_register_send_sync_msg(struct icnss_priv *priv) Loading Loading @@ -201,6 +206,11 @@ int wlfw_device_info_send_msg(struct icnss_priv *priv); int wlfw_wlan_mode_send_sync_msg(struct icnss_priv *priv, enum wlfw_driver_mode_enum_v01 mode); int icnss_wlfw_bdf_dnld_send_sync(struct icnss_priv *priv, u32 bdf_type); int icnss_wlfw_qdss_dnld_send_sync(struct icnss_priv *priv); int icnss_wlfw_qdss_data_send_sync(struct icnss_priv *priv, char *file_name, u32 total_size); int wlfw_qdss_trace_start(struct icnss_priv *priv); int wlfw_qdss_trace_stop(struct icnss_priv *priv, unsigned long long option); int wlfw_qdss_trace_mem_info_send_sync(struct icnss_priv *priv); int wlfw_power_save_send_msg(struct icnss_priv *priv, enum wlfw_power_save_mode_v01 mode); Loading