Loading drivers/net/wireless/ath/ath10k/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ ath10k_pci-y += pci.o \ obj-$(CONFIG_ATH10K_TARGET_SNOC) += ath10k_snoc.o ath10k_snoc-y += snoc.o \ qmi.o \ wcn3990_qmi_service_v01.o \ ce.o ath10k_pci-$(CONFIG_ATH10K_AHB) += ahb.o Loading drivers/net/wireless/ath/ath10k/qmi.c +636 −1 Original line number Diff line number Diff line Loading @@ -12,10 +12,14 @@ #include <soc/qcom/subsystem_notif.h> #include <soc/qcom/subsystem_restart.h> #include <soc/qcom/service-notifier.h> #include <soc/qcom/msm_qmi_interface.h> #include <soc/qcom/service-locator.h> #include "core.h" #include "qmi.h" #include "snoc.h" #include <soc/qcom/icnss.h> #include "wcn3990_qmi_service_v01.h" static DECLARE_WAIT_QUEUE_HEAD(ath10k_fw_ready_wait_event); static int ath10k_snoc_service_notifier_notify(struct notifier_block *nb, Loading Loading @@ -228,3 +232,634 @@ int ath10k_snoc_modem_ssr_unregister_notifier(struct ath10k *ar) return 0; } static char * ath10k_snoc_driver_event_to_str(enum ath10k_snoc_driver_event_type type) { switch (type) { case ATH10K_SNOC_DRIVER_EVENT_SERVER_ARRIVE: return "SERVER_ARRIVE"; case ATH10K_SNOC_DRIVER_EVENT_SERVER_EXIT: return "SERVER_EXIT"; case ATH10K_SNOC_DRIVER_EVENT_FW_READY_IND: return "FW_READY"; case ATH10K_SNOC_DRIVER_EVENT_MAX: return "EVENT_MAX"; } return "UNKNOWN"; }; static int ath10k_snoc_driver_event_post(enum ath10k_snoc_driver_event_type type, u32 flags, void *data) { int ret = 0; int i = 0; struct ath10k *ar = (struct ath10k *)data; struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); struct ath10k_snoc_qmi_config *qmi_cfg = &ar_snoc->qmi_cfg; ath10k_dbg(ar, ATH10K_DBG_SNOC, "Posting event: %s type: %d\n", ath10k_snoc_driver_event_to_str(type), type); if (type >= ATH10K_SNOC_DRIVER_EVENT_MAX) { ath10k_err(ar, "Invalid Event type: %d, can't post", type); return -EINVAL; } spin_lock_bh(&qmi_cfg->event_lock); for (i = 0; i < ATH10K_SNOC_DRIVER_EVENT_MAX; i++) { if (atomic_read(&qmi_cfg->qmi_ev_list[i].event_handled)) { qmi_cfg->qmi_ev_list[i].type = type; qmi_cfg->qmi_ev_list[i].data = data; init_completion(&qmi_cfg->qmi_ev_list[i].complete); qmi_cfg->qmi_ev_list[i].ret = ATH10K_SNOC_EVENT_PENDING; qmi_cfg->qmi_ev_list[i].sync = !!(flags & ATH10K_SNOC_EVENT_SYNC); atomic_set(&qmi_cfg->qmi_ev_list[i].event_handled, 0); list_add_tail(&qmi_cfg->qmi_ev_list[i].list, &qmi_cfg->event_list); break; } } if (i >= ATH10K_SNOC_DRIVER_EVENT_MAX) i = ATH10K_SNOC_DRIVER_EVENT_SERVER_ARRIVE; spin_unlock_bh(&qmi_cfg->event_lock); queue_work(qmi_cfg->event_wq, &qmi_cfg->event_work); if (!(flags & ATH10K_SNOC_EVENT_SYNC)) goto out; if (flags & ATH10K_SNOC_EVENT_UNINTERRUPTIBLE) wait_for_completion(&qmi_cfg->qmi_ev_list[i].complete); else ret = wait_for_completion_interruptible( &qmi_cfg->qmi_ev_list[i].complete); ath10k_dbg(ar, ATH10K_DBG_SNOC, "Completed event: %s(%d)\n", ath10k_snoc_driver_event_to_str(type), type); spin_lock_bh(&qmi_cfg->event_lock); if (ret == -ERESTARTSYS && qmi_cfg->qmi_ev_list[i].ret == ATH10K_SNOC_EVENT_PENDING) { qmi_cfg->qmi_ev_list[i].sync = false; atomic_set(&qmi_cfg->qmi_ev_list[i].event_handled, 1); spin_unlock_bh(&qmi_cfg->event_lock); ret = -EINTR; goto out; } spin_unlock_bh(&qmi_cfg->event_lock); out: return ret; } static int ath10k_snoc_wlan_mode_send_sync_msg(struct ath10k *ar, enum wlfw_driver_mode_enum_v01 mode) { int ret; struct wlfw_wlan_mode_req_msg_v01 req; struct wlfw_wlan_mode_resp_msg_v01 resp; struct msg_desc req_desc, resp_desc; struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); struct ath10k_snoc_qmi_config *qmi_cfg = &ar_snoc->qmi_cfg; if (!qmi_cfg || !qmi_cfg->wlfw_clnt) return -ENODEV; ath10k_dbg(ar, ATH10K_DBG_SNOC, "Sending Mode request, mode: %d\n", mode); memset(&req, 0, sizeof(req)); memset(&resp, 0, sizeof(resp)); req.mode = mode; req.hw_debug_valid = 1; req.hw_debug = 0; req_desc.max_msg_len = WLFW_WLAN_MODE_REQ_MSG_V01_MAX_MSG_LEN; req_desc.msg_id = QMI_WLFW_WLAN_MODE_REQ_V01; req_desc.ei_array = wlfw_wlan_mode_req_msg_v01_ei; resp_desc.max_msg_len = WLFW_WLAN_MODE_RESP_MSG_V01_MAX_MSG_LEN; resp_desc.msg_id = QMI_WLFW_WLAN_MODE_RESP_V01; resp_desc.ei_array = wlfw_wlan_mode_resp_msg_v01_ei; ret = qmi_send_req_wait(qmi_cfg->wlfw_clnt, &req_desc, &req, sizeof(req), &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS); if (ret < 0) { ath10k_err(ar, "Send mode req failed, mode: %d ret: %d\n", mode, ret); return ret; } if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { ath10k_err(ar, "QMI mode request rejected:"); ath10k_err(ar, "mode:%d result:%d error:%d\n", mode, resp.resp.result, resp.resp.error); ret = resp.resp.result; return ret; } ath10k_dbg(ar, ATH10K_DBG_SNOC, "wlan Mode request send success, mode: %d\n", mode); return 0; } static int ath10k_snoc_wlan_cfg_send_sync_msg(struct ath10k *ar, struct wlfw_wlan_cfg_req_msg_v01 *data) { int ret; struct wlfw_wlan_cfg_req_msg_v01 req; struct wlfw_wlan_cfg_resp_msg_v01 resp; struct msg_desc req_desc, resp_desc; struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); struct ath10k_snoc_qmi_config *qmi_cfg = &ar_snoc->qmi_cfg; if (!qmi_cfg || !qmi_cfg->wlfw_clnt) return -ENODEV; ath10k_dbg(ar, ATH10K_DBG_SNOC, "Sending config request\n"); memset(&req, 0, sizeof(req)); memset(&resp, 0, sizeof(resp)); memcpy(&req, data, sizeof(req)); req_desc.max_msg_len = WLFW_WLAN_CFG_REQ_MSG_V01_MAX_MSG_LEN; req_desc.msg_id = QMI_WLFW_WLAN_CFG_REQ_V01; req_desc.ei_array = wlfw_wlan_cfg_req_msg_v01_ei; resp_desc.max_msg_len = WLFW_WLAN_CFG_RESP_MSG_V01_MAX_MSG_LEN; resp_desc.msg_id = QMI_WLFW_WLAN_CFG_RESP_V01; resp_desc.ei_array = wlfw_wlan_cfg_resp_msg_v01_ei; ret = qmi_send_req_wait(qmi_cfg->wlfw_clnt, &req_desc, &req, sizeof(req), &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS); if (ret < 0) { ath10k_err(ar, "Send config req failed %d\n", ret); return ret; } if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { ath10k_err(ar, "QMI config request rejected:"); ath10k_err(ar, "result:%d error:%d\n", resp.resp.result, resp.resp.error); ret = resp.resp.result; return ret; } ath10k_dbg(ar, ATH10K_DBG_SNOC, "wlan config request success..\n"); return 0; } int ath10k_snoc_qmi_wlan_enable(struct ath10k *ar, struct ath10k_wlan_enable_cfg *config, enum ath10k_driver_mode mode, const char *host_version) { struct wlfw_wlan_cfg_req_msg_v01 req; u32 i; int ret; struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); struct ath10k_snoc_qmi_config *qmi_cfg = &ar_snoc->qmi_cfg; ath10k_dbg(ar, ATH10K_DBG_SNOC, "Mode: %d, config: %p, host_version: %s\n", mode, config, host_version); memset(&req, 0, sizeof(req)); if (!config || !host_version) { ath10k_err(ar, "WLAN_EN Config Invalid:%p: host_version:%p\n", config, host_version); ret = -EINVAL; return ret; } wait_event_timeout(ath10k_fw_ready_wait_event, (atomic_read(&qmi_cfg->fw_ready) && atomic_read(&qmi_cfg->server_connected)), msecs_to_jiffies(ATH10K_SNOC_WLAN_FW_READY_TIMEOUT)); req.host_version_valid = 1; strlcpy(req.host_version, host_version, QMI_WLFW_MAX_STR_LEN_V01 + 1); req.tgt_cfg_valid = 1; if (config->num_ce_tgt_cfg > QMI_WLFW_MAX_NUM_CE_V01) req.tgt_cfg_len = QMI_WLFW_MAX_NUM_CE_V01; else req.tgt_cfg_len = config->num_ce_tgt_cfg; for (i = 0; i < req.tgt_cfg_len; i++) { req.tgt_cfg[i].pipe_num = config->ce_tgt_cfg[i].pipe_num; req.tgt_cfg[i].pipe_dir = config->ce_tgt_cfg[i].pipe_dir; req.tgt_cfg[i].nentries = config->ce_tgt_cfg[i].nentries; req.tgt_cfg[i].nbytes_max = config->ce_tgt_cfg[i].nbytes_max; req.tgt_cfg[i].flags = config->ce_tgt_cfg[i].flags; } req.svc_cfg_valid = 1; if (config->num_ce_svc_pipe_cfg > QMI_WLFW_MAX_NUM_SVC_V01) req.svc_cfg_len = QMI_WLFW_MAX_NUM_SVC_V01; else req.svc_cfg_len = config->num_ce_svc_pipe_cfg; for (i = 0; i < req.svc_cfg_len; i++) { req.svc_cfg[i].service_id = config->ce_svc_cfg[i].service_id; req.svc_cfg[i].pipe_dir = config->ce_svc_cfg[i].pipe_dir; req.svc_cfg[i].pipe_num = config->ce_svc_cfg[i].pipe_num; } req.shadow_reg_valid = 1; if (config->num_shadow_reg_cfg > QMI_WLFW_MAX_NUM_SHADOW_REG_V01) req.shadow_reg_len = QMI_WLFW_MAX_NUM_SHADOW_REG_V01; else req.shadow_reg_len = config->num_shadow_reg_cfg; memcpy(req.shadow_reg, config->shadow_reg_cfg, sizeof(struct wlfw_shadow_reg_cfg_s_v01) * req.shadow_reg_len); ret = ath10k_snoc_wlan_cfg_send_sync_msg(ar, &req); if (ret) { ath10k_err(ar, "WLAN config send failed\n"); return ret; } ret = ath10k_snoc_wlan_mode_send_sync_msg(ar, mode); if (ret) { ath10k_err(ar, "WLAN mode send failed\n"); return ret; } return 0; } int ath10k_snoc_qmi_wlan_disable(struct ath10k *ar) { return ath10k_snoc_wlan_mode_send_sync_msg(ar, QMI_WLFW_OFF_V01); } static int ath10k_snoc_ind_register_send_sync_msg(struct ath10k *ar) { int ret; struct wlfw_ind_register_req_msg_v01 req; struct wlfw_ind_register_resp_msg_v01 resp; struct msg_desc req_desc, resp_desc; struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); struct ath10k_snoc_qmi_config *qmi_cfg = &ar_snoc->qmi_cfg; ath10k_dbg(ar, ATH10K_DBG_SNOC, "Sending indication register message,\n"); memset(&req, 0, sizeof(req)); memset(&resp, 0, sizeof(resp)); req.client_id_valid = 1; req.client_id = WLFW_CLIENT_ID; req.fw_ready_enable_valid = 1; req.fw_ready_enable = 1; req.msa_ready_enable_valid = 1; req.msa_ready_enable = 1; req_desc.max_msg_len = WLFW_IND_REGISTER_REQ_MSG_V01_MAX_MSG_LEN; req_desc.msg_id = QMI_WLFW_IND_REGISTER_REQ_V01; req_desc.ei_array = wlfw_ind_register_req_msg_v01_ei; resp_desc.max_msg_len = WLFW_IND_REGISTER_RESP_MSG_V01_MAX_MSG_LEN; resp_desc.msg_id = QMI_WLFW_IND_REGISTER_RESP_V01; resp_desc.ei_array = wlfw_ind_register_resp_msg_v01_ei; ret = qmi_send_req_wait(qmi_cfg->wlfw_clnt, &req_desc, &req, sizeof(req), &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS); if (ret < 0) { ath10k_err(ar, "Send indication register req failed %d\n", ret); return ret; } if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { ath10k_err(ar, "QMI indication register request rejected:"); ath10k_err(ar, "resut:%d error:%d\n", resp.resp.result, resp.resp.error); ret = resp.resp.result; return ret; } return 0; } static void ath10k_snoc_qmi_wlfw_clnt_notify_work(struct work_struct *work) { int ret; struct ath10k_snoc_qmi_config *qmi_cfg = container_of(work, struct ath10k_snoc_qmi_config, qmi_recv_msg_work); struct ath10k_snoc *ar_snoc = container_of(qmi_cfg, struct ath10k_snoc, qmi_cfg); struct ath10k *ar = ar_snoc->ar; ath10k_dbg(ar, ATH10K_DBG_SNOC, "Receiving Event in work queue context\n"); do { } while ((ret = qmi_recv_msg(qmi_cfg->wlfw_clnt)) == 0); if (ret != -ENOMSG) ath10k_err(ar, "Error receiving message: %d\n", ret); ath10k_dbg(ar, ATH10K_DBG_SNOC, "Receiving Event completed\n"); } static void ath10k_snoc_qmi_wlfw_clnt_notify(struct qmi_handle *handle, enum qmi_event_type event, void *notify_priv) { struct ath10k_snoc_qmi_config *qmi_cfg = (struct ath10k_snoc_qmi_config *)notify_priv; struct ath10k_snoc *ar_snoc = container_of(qmi_cfg, struct ath10k_snoc, qmi_cfg); struct ath10k *ar = ar_snoc->ar; ath10k_dbg(ar, ATH10K_DBG_SNOC, "QMI client notify: %d\n", event); if (!qmi_cfg || !qmi_cfg->wlfw_clnt) return; switch (event) { case QMI_RECV_MSG: schedule_work(&qmi_cfg->qmi_recv_msg_work); break; default: ath10k_dbg(ar, ATH10K_DBG_SNOC, "Unknown Event: %d\n", event); break; } } static void ath10k_snoc_qmi_wlfw_clnt_ind(struct qmi_handle *handle, unsigned int msg_id, void *msg, unsigned int msg_len, void *ind_cb_priv) { struct ath10k_snoc_qmi_config *qmi_cfg = (struct ath10k_snoc_qmi_config *)ind_cb_priv; struct ath10k_snoc *ar_snoc = container_of(qmi_cfg, struct ath10k_snoc, qmi_cfg); struct ath10k *ar = ar_snoc->ar; ath10k_dbg(ar, ATH10K_DBG_SNOC, "Received Ind 0x%x, msg_len: %d\n", msg_id, msg_len); switch (msg_id) { case QMI_WLFW_FW_READY_IND_V01: ath10k_snoc_driver_event_post( ATH10K_SNOC_DRIVER_EVENT_FW_READY_IND, 0, ar); break; case QMI_WLFW_MSA_READY_IND_V01: qmi_cfg->msa_ready = true; ath10k_dbg(ar, ATH10K_DBG_SNOC, "Received MSA Ready, ind = 0x%x\n", msg_id); break; default: ath10k_err(ar, "Invalid msg_id 0x%x\n", msg_id); break; } } static int ath10k_snoc_driver_event_server_arrive(struct ath10k *ar) { int ret = 0; struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); struct ath10k_snoc_qmi_config *qmi_cfg = &ar_snoc->qmi_cfg; if (!qmi_cfg) return -ENODEV; qmi_cfg->wlfw_clnt = qmi_handle_create( ath10k_snoc_qmi_wlfw_clnt_notify, qmi_cfg); if (!qmi_cfg->wlfw_clnt) { ath10k_dbg(ar, ATH10K_DBG_SNOC, "QMI client handle create failed\n"); return -ENOMEM; } ret = qmi_connect_to_service(qmi_cfg->wlfw_clnt, WLFW_SERVICE_ID_V01, WLFW_SERVICE_VERS_V01, WLFW_SERVICE_INS_ID_V01); if (ret < 0) { ath10k_err(ar, "QMI WLAN Service not found : %d\n", ret); goto err_qmi_config; } ret = qmi_register_ind_cb(qmi_cfg->wlfw_clnt, ath10k_snoc_qmi_wlfw_clnt_ind, qmi_cfg); if (ret < 0) { ath10k_err(ar, "Failed to register indication callback: %d\n", ret); goto err_qmi_config; } ret = ath10k_snoc_ind_register_send_sync_msg(ar); if (ret) { ath10k_err(ar, "Failed to config qmi ind register\n"); goto err_qmi_config; } atomic_set(&qmi_cfg->server_connected, 1); wake_up_all(&ath10k_fw_ready_wait_event); ath10k_dbg(ar, ATH10K_DBG_SNOC, "QMI Server Arrive Configuration Success\n"); return 0; err_qmi_config: qmi_handle_destroy(qmi_cfg->wlfw_clnt); qmi_cfg->wlfw_clnt = NULL; return ret; } static int ath10k_snoc_driver_event_server_exit(struct ath10k *ar) { struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); struct ath10k_snoc_qmi_config *qmi_cfg = &ar_snoc->qmi_cfg; ath10k_dbg(ar, ATH10K_DBG_SNOC, "QMI Server Exit event received\n"); atomic_set(&qmi_cfg->fw_ready, 0); qmi_cfg->msa_ready = false; atomic_set(&qmi_cfg->server_connected, 0); return 0; } static int ath10k_snoc_driver_event_fw_ready_ind(struct ath10k *ar) { struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); struct ath10k_snoc_qmi_config *qmi_cfg = &ar_snoc->qmi_cfg; ath10k_dbg(ar, ATH10K_DBG_SNOC, "FW Ready event received.\n"); atomic_set(&qmi_cfg->fw_ready, 1); wake_up_all(&ath10k_fw_ready_wait_event); return 0; } static void ath10k_snoc_driver_event_work(struct work_struct *work) { struct ath10k_snoc_qmi_driver_event *event; int ret; struct ath10k_snoc_qmi_config *qmi_cfg = container_of(work, struct ath10k_snoc_qmi_config, event_work); struct ath10k_snoc *ar_snoc = container_of(qmi_cfg, struct ath10k_snoc, qmi_cfg); struct ath10k *ar = ar_snoc->ar; spin_lock_bh(&qmi_cfg->event_lock); while (!list_empty(&qmi_cfg->event_list)) { event = list_first_entry(&qmi_cfg->event_list, struct ath10k_snoc_qmi_driver_event, list); list_del(&event->list); spin_unlock_bh(&qmi_cfg->event_lock); ath10k_dbg(ar, ATH10K_DBG_SNOC, "Processing event: %s%s(%d)\n", ath10k_snoc_driver_event_to_str(event->type), event->sync ? "-sync" : "", event->type); switch (event->type) { case ATH10K_SNOC_DRIVER_EVENT_SERVER_ARRIVE: ret = ath10k_snoc_driver_event_server_arrive(ar); break; case ATH10K_SNOC_DRIVER_EVENT_SERVER_EXIT: ret = ath10k_snoc_driver_event_server_exit(ar); break; case ATH10K_SNOC_DRIVER_EVENT_FW_READY_IND: ret = ath10k_snoc_driver_event_fw_ready_ind(ar); break; default: ath10k_err(ar, "Invalid Event type: %d", event->type); kfree(event); continue; } atomic_set(&event->event_handled, 1); ath10k_dbg(ar, ATH10K_DBG_SNOC, "Event Processed: %s%s(%d), ret: %d\n", ath10k_snoc_driver_event_to_str(event->type), event->sync ? "-sync" : "", event->type, ret); spin_lock_bh(&qmi_cfg->event_lock); if (event->sync) { event->ret = ret; complete(&event->complete); continue; } spin_unlock_bh(&qmi_cfg->event_lock); spin_lock_bh(&qmi_cfg->event_lock); } spin_unlock_bh(&qmi_cfg->event_lock); } static int ath10k_snoc_qmi_wlfw_clnt_svc_event_notify(struct notifier_block *this, unsigned long code, void *_cmd) { int ret = 0; struct ath10k_snoc_qmi_config *qmi_cfg = container_of(this, struct ath10k_snoc_qmi_config, wlfw_clnt_nb); struct ath10k_snoc *ar_snoc = container_of(qmi_cfg, struct ath10k_snoc, qmi_cfg); struct ath10k *ar = ar_snoc->ar; ath10k_dbg(ar, ATH10K_DBG_SNOC, "Event Notify: code: %ld", code); switch (code) { case QMI_SERVER_ARRIVE: ret = ath10k_snoc_driver_event_post( ATH10K_SNOC_DRIVER_EVENT_SERVER_ARRIVE, 0, ar); break; case QMI_SERVER_EXIT: ret = ath10k_snoc_driver_event_post( ATH10K_SNOC_DRIVER_EVENT_SERVER_EXIT, 0, ar); break; default: ath10k_err(ar, "Invalid code: %ld", code); break; } return ret; } int ath10k_snoc_start_qmi_service(struct ath10k *ar) { int ret; int i; struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); struct ath10k_snoc_qmi_config *qmi_cfg = &ar_snoc->qmi_cfg; qmi_cfg->event_wq = alloc_workqueue("ath10k_snoc_driver_event", WQ_UNBOUND, 1); if (!qmi_cfg->event_wq) { ath10k_err(ar, "Workqueue creation failed\n"); return -EFAULT; } spin_lock_init(&qmi_cfg->event_lock); atomic_set(&qmi_cfg->fw_ready, 0); atomic_set(&qmi_cfg->server_connected, 0); INIT_WORK(&qmi_cfg->event_work, ath10k_snoc_driver_event_work); INIT_WORK(&qmi_cfg->qmi_recv_msg_work, ath10k_snoc_qmi_wlfw_clnt_notify_work); INIT_LIST_HEAD(&qmi_cfg->event_list); for (i = 0; i < ATH10K_SNOC_DRIVER_EVENT_MAX; i++) atomic_set(&qmi_cfg->qmi_ev_list[i].event_handled, 1); qmi_cfg->wlfw_clnt_nb.notifier_call = ath10k_snoc_qmi_wlfw_clnt_svc_event_notify; ret = qmi_svc_event_notifier_register(WLFW_SERVICE_ID_V01, WLFW_SERVICE_VERS_V01, WLFW_SERVICE_INS_ID_V01, &qmi_cfg->wlfw_clnt_nb); if (ret < 0) { ath10k_err(ar, "Notifier register failed: %d\n", ret); ret = -EFAULT; goto out_destroy_wq; } atomic_set(&qmi_cfg->fw_ready, 1); ath10k_dbg(ar, ATH10K_DBG_SNOC, "QMI service started successfully\n"); return 0; out_destroy_wq: destroy_workqueue(qmi_cfg->event_wq); return ret; } void ath10k_snoc_stop_qmi_service(struct ath10k *ar) { struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); struct ath10k_snoc_qmi_config *qmi_cfg = &ar_snoc->qmi_cfg; ath10k_dbg(ar, ATH10K_DBG_SNOC, "Removing QMI service..\n"); qmi_svc_event_notifier_unregister(WLFW_SERVICE_ID_V01, WLFW_SERVICE_VERS_V01, WLFW_SERVICE_INS_ID_V01, &qmi_cfg->wlfw_clnt_nb); wake_up_all(&ath10k_fw_ready_wait_event); destroy_workqueue(qmi_cfg->event_wq); qmi_cfg = NULL; } drivers/net/wireless/ath/ath10k/qmi.h +138 −1 Original line number Diff line number Diff line Loading @@ -11,9 +11,146 @@ */ #ifndef _QMI_H_ #define _QMI_H_ #define ATH10K_SNOC_EVENT_PENDING 2989 #define ATH10K_SNOC_EVENT_SYNC BIT(0) #define ATH10K_SNOC_EVENT_UNINTERRUPTIBLE BIT(1) #define ATH10K_SNOC_WLAN_FW_READY_TIMEOUT 8000 #define WLFW_SERVICE_INS_ID_V01 0 #define WLFW_CLIENT_ID 0x4b4e454c #define WLFW_TIMEOUT_MS 20000 enum ath10k_snoc_driver_event_type { ATH10K_SNOC_DRIVER_EVENT_SERVER_ARRIVE, ATH10K_SNOC_DRIVER_EVENT_SERVER_EXIT, ATH10K_SNOC_DRIVER_EVENT_FW_READY_IND, ATH10K_SNOC_DRIVER_EVENT_MAX, }; /* enum ath10k_driver_mode: ath10k driver mode * @ATH10K_MISSION: mission mode * @ATH10K_FTM: ftm mode * @ATH10K_EPPING: epping mode * @ATH10K_OFF: off mode */ enum ath10k_driver_mode { ATH10K_MISSION, ATH10K_FTM, ATH10K_EPPING, ATH10K_OFF }; /* struct ath10k_ce_tgt_pipe_cfg: target pipe configuration * @pipe_num: pipe number * @pipe_dir: pipe direction * @nentries: entries in pipe * @nbytes_max: pipe max size * @flags: pipe flags * @reserved: reserved */ struct ath10k_ce_tgt_pipe_cfg { u32 pipe_num; u32 pipe_dir; u32 nentries; u32 nbytes_max; u32 flags; u32 reserved; }; /* struct ath10k_ce_svc_pipe_cfg: service pipe configuration * @service_id: target version * @pipe_dir: pipe direction * @pipe_num: pipe number */ struct ath10k_ce_svc_pipe_cfg { u32 service_id; u32 pipe_dir; u32 pipe_num; }; /* struct ath10k_shadow_reg_cfg: shadow register configuration * @ce_id: copy engine id * @reg_offset: offset to copy engine */ struct ath10k_shadow_reg_cfg { u16 ce_id; u16 reg_offset; }; /* struct ath10k_wlan_enable_cfg: wlan enable configuration * @num_ce_tgt_cfg: no of ce target configuration * @ce_tgt_cfg: target ce configuration * @num_ce_svc_pipe_cfg: no of ce service configuration * @ce_svc_cfg: ce service configuration * @num_shadow_reg_cfg: no of shadow registers * @shadow_reg_cfg: shadow register configuration */ struct ath10k_wlan_enable_cfg { u32 num_ce_tgt_cfg; struct ath10k_ce_tgt_pipe_cfg *ce_tgt_cfg; u32 num_ce_svc_pipe_cfg; struct ath10k_ce_svc_pipe_cfg *ce_svc_cfg; u32 num_shadow_reg_cfg; struct ath10k_shadow_reg_cfg *shadow_reg_cfg; }; /* struct ath10k_snoc_qmi_driver_event: qmi driver event * event_handled: event handled by event work handler * sync: event synced * ret: event received return value * list: list to queue qmi event for process * type: driver event type * complete: completion for event handle complete * data: encapsulate driver data for event handler callback */ struct ath10k_snoc_qmi_driver_event { atomic_t event_handled; bool sync; int ret; struct list_head list; enum ath10k_snoc_driver_event_type type; struct completion complete; void *data; }; /* struct ath10k_snoc_qmi_config: qmi service configuration * fw_ready: wlan firmware ready for wlan operation * msa_ready: wlan firmware msa memory ready for board data download * server_connected: qmi server connected * event_work: QMI event work * event_list: QMI event list * qmi_recv_msg_work: QMI message receive work * event_wq: QMI event work queue * wlfw_clnt_nb: WLAN firmware indication callback * wlfw_clnt: QMI notifier handler for wlan firmware * qmi_ev_list: QMI event list * event_lock: spinlock for qmi event work queue */ struct ath10k_snoc_qmi_config { atomic_t fw_ready; bool msa_ready; atomic_t server_connected; struct work_struct event_work; struct list_head event_list; struct work_struct qmi_recv_msg_work; struct workqueue_struct *event_wq; struct notifier_block wlfw_clnt_nb; struct qmi_handle *wlfw_clnt; struct ath10k_snoc_qmi_driver_event qmi_ev_list[ATH10K_SNOC_DRIVER_EVENT_MAX]; spinlock_t event_lock; /* spinlock for qmi event work queue */ }; int ath10k_snoc_pd_restart_enable(struct ath10k *ar); int ath10k_snoc_modem_ssr_register_notifier(struct ath10k *ar); int ath10k_snoc_modem_ssr_unregister_notifier(struct ath10k *ar); int ath10k_snoc_pdr_unregister_notifier(struct ath10k *ar); int ath10k_snoc_start_qmi_service(struct ath10k *ar); void ath10k_snoc_stop_qmi_service(struct ath10k *ar); int ath10k_snoc_qmi_wlan_enable(struct ath10k *ar, struct ath10k_wlan_enable_cfg *config, enum ath10k_driver_mode mode, const char *host_version); int ath10k_snoc_qmi_wlan_disable(struct ath10k *ar); #endif drivers/net/wireless/ath/ath10k/snoc.c +19 −15 File changed.Preview size limit exceeded, changes collapsed. Show changes drivers/net/wireless/ath/ath10k/snoc.h +2 −67 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ #include "hw.h" #include "ce.h" #include "pci.h" #include "qmi.h" #include <soc/qcom/service-locator.h> #define ATH10K_SNOC_RX_POST_RETRY_MS 50 #define CE_POLL_PIPE 4 Loading Loading @@ -143,60 +144,7 @@ struct ath10k_snoc { int total_domains; struct notifier_block get_service_nb; atomic_t fw_crashed; }; /* struct ath10k_ce_tgt_pipe_cfg: target pipe configuration * @pipe_num: pipe number * @pipe_dir: pipe direction * @nentries: entries in pipe * @nbytes_max: pipe max size * @flags: pipe flags * @reserved: reserved */ struct ath10k_ce_tgt_pipe_cfg { u32 pipe_num; u32 pipe_dir; u32 nentries; u32 nbytes_max; u32 flags; u32 reserved; }; /* struct ath10k_ce_svc_pipe_cfg: service pipe configuration * @service_id: target version * @pipe_dir: pipe direction * @pipe_num: pipe number */ struct ath10k_ce_svc_pipe_cfg { u32 service_id; u32 pipe_dir; u32 pipe_num; }; /* struct ath10k_shadow_reg_cfg: shadow register configuration * @ce_id: copy engine id * @reg_offset: offset to copy engine */ struct ath10k_shadow_reg_cfg { u16 ce_id; u16 reg_offset; }; /* struct ath10k_wlan_enable_cfg: wlan enable configuration * @num_ce_tgt_cfg: no of ce target configuration * @ce_tgt_cfg: target ce configuration * @num_ce_svc_pipe_cfg: no of ce service configuration * @ce_svc_cfg: ce service configuration * @num_shadow_reg_cfg: no of shadow registers * @shadow_reg_cfg: shadow register configuration */ struct ath10k_wlan_enable_cfg { u32 num_ce_tgt_cfg; struct ath10k_ce_tgt_pipe_cfg *ce_tgt_cfg; u32 num_ce_svc_pipe_cfg; struct ath10k_ce_svc_pipe_cfg *ce_svc_cfg; u32 num_shadow_reg_cfg; struct ath10k_shadow_reg_cfg *shadow_reg_cfg; struct ath10k_snoc_qmi_config qmi_cfg; }; struct ath10k_event_pd_down_data { Loading @@ -204,19 +152,6 @@ struct ath10k_event_pd_down_data { bool fw_rejuvenate; }; /* enum ath10k_driver_mode: ath10k driver mode * @ATH10K_MISSION: mission mode * @ATH10K_FTM: ftm mode * @ATH10K_EPPING: epping mode * @ATH10K_OFF: off mode */ enum ath10k_driver_mode { ATH10K_MISSION, ATH10K_FTM, ATH10K_EPPING, ATH10K_OFF }; static inline struct ath10k_snoc *ath10k_snoc_priv(struct ath10k *ar) { return (struct ath10k_snoc *)ar->drv_priv; Loading Loading
drivers/net/wireless/ath/ath10k/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ ath10k_pci-y += pci.o \ obj-$(CONFIG_ATH10K_TARGET_SNOC) += ath10k_snoc.o ath10k_snoc-y += snoc.o \ qmi.o \ wcn3990_qmi_service_v01.o \ ce.o ath10k_pci-$(CONFIG_ATH10K_AHB) += ahb.o Loading
drivers/net/wireless/ath/ath10k/qmi.c +636 −1 Original line number Diff line number Diff line Loading @@ -12,10 +12,14 @@ #include <soc/qcom/subsystem_notif.h> #include <soc/qcom/subsystem_restart.h> #include <soc/qcom/service-notifier.h> #include <soc/qcom/msm_qmi_interface.h> #include <soc/qcom/service-locator.h> #include "core.h" #include "qmi.h" #include "snoc.h" #include <soc/qcom/icnss.h> #include "wcn3990_qmi_service_v01.h" static DECLARE_WAIT_QUEUE_HEAD(ath10k_fw_ready_wait_event); static int ath10k_snoc_service_notifier_notify(struct notifier_block *nb, Loading Loading @@ -228,3 +232,634 @@ int ath10k_snoc_modem_ssr_unregister_notifier(struct ath10k *ar) return 0; } static char * ath10k_snoc_driver_event_to_str(enum ath10k_snoc_driver_event_type type) { switch (type) { case ATH10K_SNOC_DRIVER_EVENT_SERVER_ARRIVE: return "SERVER_ARRIVE"; case ATH10K_SNOC_DRIVER_EVENT_SERVER_EXIT: return "SERVER_EXIT"; case ATH10K_SNOC_DRIVER_EVENT_FW_READY_IND: return "FW_READY"; case ATH10K_SNOC_DRIVER_EVENT_MAX: return "EVENT_MAX"; } return "UNKNOWN"; }; static int ath10k_snoc_driver_event_post(enum ath10k_snoc_driver_event_type type, u32 flags, void *data) { int ret = 0; int i = 0; struct ath10k *ar = (struct ath10k *)data; struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); struct ath10k_snoc_qmi_config *qmi_cfg = &ar_snoc->qmi_cfg; ath10k_dbg(ar, ATH10K_DBG_SNOC, "Posting event: %s type: %d\n", ath10k_snoc_driver_event_to_str(type), type); if (type >= ATH10K_SNOC_DRIVER_EVENT_MAX) { ath10k_err(ar, "Invalid Event type: %d, can't post", type); return -EINVAL; } spin_lock_bh(&qmi_cfg->event_lock); for (i = 0; i < ATH10K_SNOC_DRIVER_EVENT_MAX; i++) { if (atomic_read(&qmi_cfg->qmi_ev_list[i].event_handled)) { qmi_cfg->qmi_ev_list[i].type = type; qmi_cfg->qmi_ev_list[i].data = data; init_completion(&qmi_cfg->qmi_ev_list[i].complete); qmi_cfg->qmi_ev_list[i].ret = ATH10K_SNOC_EVENT_PENDING; qmi_cfg->qmi_ev_list[i].sync = !!(flags & ATH10K_SNOC_EVENT_SYNC); atomic_set(&qmi_cfg->qmi_ev_list[i].event_handled, 0); list_add_tail(&qmi_cfg->qmi_ev_list[i].list, &qmi_cfg->event_list); break; } } if (i >= ATH10K_SNOC_DRIVER_EVENT_MAX) i = ATH10K_SNOC_DRIVER_EVENT_SERVER_ARRIVE; spin_unlock_bh(&qmi_cfg->event_lock); queue_work(qmi_cfg->event_wq, &qmi_cfg->event_work); if (!(flags & ATH10K_SNOC_EVENT_SYNC)) goto out; if (flags & ATH10K_SNOC_EVENT_UNINTERRUPTIBLE) wait_for_completion(&qmi_cfg->qmi_ev_list[i].complete); else ret = wait_for_completion_interruptible( &qmi_cfg->qmi_ev_list[i].complete); ath10k_dbg(ar, ATH10K_DBG_SNOC, "Completed event: %s(%d)\n", ath10k_snoc_driver_event_to_str(type), type); spin_lock_bh(&qmi_cfg->event_lock); if (ret == -ERESTARTSYS && qmi_cfg->qmi_ev_list[i].ret == ATH10K_SNOC_EVENT_PENDING) { qmi_cfg->qmi_ev_list[i].sync = false; atomic_set(&qmi_cfg->qmi_ev_list[i].event_handled, 1); spin_unlock_bh(&qmi_cfg->event_lock); ret = -EINTR; goto out; } spin_unlock_bh(&qmi_cfg->event_lock); out: return ret; } static int ath10k_snoc_wlan_mode_send_sync_msg(struct ath10k *ar, enum wlfw_driver_mode_enum_v01 mode) { int ret; struct wlfw_wlan_mode_req_msg_v01 req; struct wlfw_wlan_mode_resp_msg_v01 resp; struct msg_desc req_desc, resp_desc; struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); struct ath10k_snoc_qmi_config *qmi_cfg = &ar_snoc->qmi_cfg; if (!qmi_cfg || !qmi_cfg->wlfw_clnt) return -ENODEV; ath10k_dbg(ar, ATH10K_DBG_SNOC, "Sending Mode request, mode: %d\n", mode); memset(&req, 0, sizeof(req)); memset(&resp, 0, sizeof(resp)); req.mode = mode; req.hw_debug_valid = 1; req.hw_debug = 0; req_desc.max_msg_len = WLFW_WLAN_MODE_REQ_MSG_V01_MAX_MSG_LEN; req_desc.msg_id = QMI_WLFW_WLAN_MODE_REQ_V01; req_desc.ei_array = wlfw_wlan_mode_req_msg_v01_ei; resp_desc.max_msg_len = WLFW_WLAN_MODE_RESP_MSG_V01_MAX_MSG_LEN; resp_desc.msg_id = QMI_WLFW_WLAN_MODE_RESP_V01; resp_desc.ei_array = wlfw_wlan_mode_resp_msg_v01_ei; ret = qmi_send_req_wait(qmi_cfg->wlfw_clnt, &req_desc, &req, sizeof(req), &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS); if (ret < 0) { ath10k_err(ar, "Send mode req failed, mode: %d ret: %d\n", mode, ret); return ret; } if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { ath10k_err(ar, "QMI mode request rejected:"); ath10k_err(ar, "mode:%d result:%d error:%d\n", mode, resp.resp.result, resp.resp.error); ret = resp.resp.result; return ret; } ath10k_dbg(ar, ATH10K_DBG_SNOC, "wlan Mode request send success, mode: %d\n", mode); return 0; } static int ath10k_snoc_wlan_cfg_send_sync_msg(struct ath10k *ar, struct wlfw_wlan_cfg_req_msg_v01 *data) { int ret; struct wlfw_wlan_cfg_req_msg_v01 req; struct wlfw_wlan_cfg_resp_msg_v01 resp; struct msg_desc req_desc, resp_desc; struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); struct ath10k_snoc_qmi_config *qmi_cfg = &ar_snoc->qmi_cfg; if (!qmi_cfg || !qmi_cfg->wlfw_clnt) return -ENODEV; ath10k_dbg(ar, ATH10K_DBG_SNOC, "Sending config request\n"); memset(&req, 0, sizeof(req)); memset(&resp, 0, sizeof(resp)); memcpy(&req, data, sizeof(req)); req_desc.max_msg_len = WLFW_WLAN_CFG_REQ_MSG_V01_MAX_MSG_LEN; req_desc.msg_id = QMI_WLFW_WLAN_CFG_REQ_V01; req_desc.ei_array = wlfw_wlan_cfg_req_msg_v01_ei; resp_desc.max_msg_len = WLFW_WLAN_CFG_RESP_MSG_V01_MAX_MSG_LEN; resp_desc.msg_id = QMI_WLFW_WLAN_CFG_RESP_V01; resp_desc.ei_array = wlfw_wlan_cfg_resp_msg_v01_ei; ret = qmi_send_req_wait(qmi_cfg->wlfw_clnt, &req_desc, &req, sizeof(req), &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS); if (ret < 0) { ath10k_err(ar, "Send config req failed %d\n", ret); return ret; } if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { ath10k_err(ar, "QMI config request rejected:"); ath10k_err(ar, "result:%d error:%d\n", resp.resp.result, resp.resp.error); ret = resp.resp.result; return ret; } ath10k_dbg(ar, ATH10K_DBG_SNOC, "wlan config request success..\n"); return 0; } int ath10k_snoc_qmi_wlan_enable(struct ath10k *ar, struct ath10k_wlan_enable_cfg *config, enum ath10k_driver_mode mode, const char *host_version) { struct wlfw_wlan_cfg_req_msg_v01 req; u32 i; int ret; struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); struct ath10k_snoc_qmi_config *qmi_cfg = &ar_snoc->qmi_cfg; ath10k_dbg(ar, ATH10K_DBG_SNOC, "Mode: %d, config: %p, host_version: %s\n", mode, config, host_version); memset(&req, 0, sizeof(req)); if (!config || !host_version) { ath10k_err(ar, "WLAN_EN Config Invalid:%p: host_version:%p\n", config, host_version); ret = -EINVAL; return ret; } wait_event_timeout(ath10k_fw_ready_wait_event, (atomic_read(&qmi_cfg->fw_ready) && atomic_read(&qmi_cfg->server_connected)), msecs_to_jiffies(ATH10K_SNOC_WLAN_FW_READY_TIMEOUT)); req.host_version_valid = 1; strlcpy(req.host_version, host_version, QMI_WLFW_MAX_STR_LEN_V01 + 1); req.tgt_cfg_valid = 1; if (config->num_ce_tgt_cfg > QMI_WLFW_MAX_NUM_CE_V01) req.tgt_cfg_len = QMI_WLFW_MAX_NUM_CE_V01; else req.tgt_cfg_len = config->num_ce_tgt_cfg; for (i = 0; i < req.tgt_cfg_len; i++) { req.tgt_cfg[i].pipe_num = config->ce_tgt_cfg[i].pipe_num; req.tgt_cfg[i].pipe_dir = config->ce_tgt_cfg[i].pipe_dir; req.tgt_cfg[i].nentries = config->ce_tgt_cfg[i].nentries; req.tgt_cfg[i].nbytes_max = config->ce_tgt_cfg[i].nbytes_max; req.tgt_cfg[i].flags = config->ce_tgt_cfg[i].flags; } req.svc_cfg_valid = 1; if (config->num_ce_svc_pipe_cfg > QMI_WLFW_MAX_NUM_SVC_V01) req.svc_cfg_len = QMI_WLFW_MAX_NUM_SVC_V01; else req.svc_cfg_len = config->num_ce_svc_pipe_cfg; for (i = 0; i < req.svc_cfg_len; i++) { req.svc_cfg[i].service_id = config->ce_svc_cfg[i].service_id; req.svc_cfg[i].pipe_dir = config->ce_svc_cfg[i].pipe_dir; req.svc_cfg[i].pipe_num = config->ce_svc_cfg[i].pipe_num; } req.shadow_reg_valid = 1; if (config->num_shadow_reg_cfg > QMI_WLFW_MAX_NUM_SHADOW_REG_V01) req.shadow_reg_len = QMI_WLFW_MAX_NUM_SHADOW_REG_V01; else req.shadow_reg_len = config->num_shadow_reg_cfg; memcpy(req.shadow_reg, config->shadow_reg_cfg, sizeof(struct wlfw_shadow_reg_cfg_s_v01) * req.shadow_reg_len); ret = ath10k_snoc_wlan_cfg_send_sync_msg(ar, &req); if (ret) { ath10k_err(ar, "WLAN config send failed\n"); return ret; } ret = ath10k_snoc_wlan_mode_send_sync_msg(ar, mode); if (ret) { ath10k_err(ar, "WLAN mode send failed\n"); return ret; } return 0; } int ath10k_snoc_qmi_wlan_disable(struct ath10k *ar) { return ath10k_snoc_wlan_mode_send_sync_msg(ar, QMI_WLFW_OFF_V01); } static int ath10k_snoc_ind_register_send_sync_msg(struct ath10k *ar) { int ret; struct wlfw_ind_register_req_msg_v01 req; struct wlfw_ind_register_resp_msg_v01 resp; struct msg_desc req_desc, resp_desc; struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); struct ath10k_snoc_qmi_config *qmi_cfg = &ar_snoc->qmi_cfg; ath10k_dbg(ar, ATH10K_DBG_SNOC, "Sending indication register message,\n"); memset(&req, 0, sizeof(req)); memset(&resp, 0, sizeof(resp)); req.client_id_valid = 1; req.client_id = WLFW_CLIENT_ID; req.fw_ready_enable_valid = 1; req.fw_ready_enable = 1; req.msa_ready_enable_valid = 1; req.msa_ready_enable = 1; req_desc.max_msg_len = WLFW_IND_REGISTER_REQ_MSG_V01_MAX_MSG_LEN; req_desc.msg_id = QMI_WLFW_IND_REGISTER_REQ_V01; req_desc.ei_array = wlfw_ind_register_req_msg_v01_ei; resp_desc.max_msg_len = WLFW_IND_REGISTER_RESP_MSG_V01_MAX_MSG_LEN; resp_desc.msg_id = QMI_WLFW_IND_REGISTER_RESP_V01; resp_desc.ei_array = wlfw_ind_register_resp_msg_v01_ei; ret = qmi_send_req_wait(qmi_cfg->wlfw_clnt, &req_desc, &req, sizeof(req), &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS); if (ret < 0) { ath10k_err(ar, "Send indication register req failed %d\n", ret); return ret; } if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { ath10k_err(ar, "QMI indication register request rejected:"); ath10k_err(ar, "resut:%d error:%d\n", resp.resp.result, resp.resp.error); ret = resp.resp.result; return ret; } return 0; } static void ath10k_snoc_qmi_wlfw_clnt_notify_work(struct work_struct *work) { int ret; struct ath10k_snoc_qmi_config *qmi_cfg = container_of(work, struct ath10k_snoc_qmi_config, qmi_recv_msg_work); struct ath10k_snoc *ar_snoc = container_of(qmi_cfg, struct ath10k_snoc, qmi_cfg); struct ath10k *ar = ar_snoc->ar; ath10k_dbg(ar, ATH10K_DBG_SNOC, "Receiving Event in work queue context\n"); do { } while ((ret = qmi_recv_msg(qmi_cfg->wlfw_clnt)) == 0); if (ret != -ENOMSG) ath10k_err(ar, "Error receiving message: %d\n", ret); ath10k_dbg(ar, ATH10K_DBG_SNOC, "Receiving Event completed\n"); } static void ath10k_snoc_qmi_wlfw_clnt_notify(struct qmi_handle *handle, enum qmi_event_type event, void *notify_priv) { struct ath10k_snoc_qmi_config *qmi_cfg = (struct ath10k_snoc_qmi_config *)notify_priv; struct ath10k_snoc *ar_snoc = container_of(qmi_cfg, struct ath10k_snoc, qmi_cfg); struct ath10k *ar = ar_snoc->ar; ath10k_dbg(ar, ATH10K_DBG_SNOC, "QMI client notify: %d\n", event); if (!qmi_cfg || !qmi_cfg->wlfw_clnt) return; switch (event) { case QMI_RECV_MSG: schedule_work(&qmi_cfg->qmi_recv_msg_work); break; default: ath10k_dbg(ar, ATH10K_DBG_SNOC, "Unknown Event: %d\n", event); break; } } static void ath10k_snoc_qmi_wlfw_clnt_ind(struct qmi_handle *handle, unsigned int msg_id, void *msg, unsigned int msg_len, void *ind_cb_priv) { struct ath10k_snoc_qmi_config *qmi_cfg = (struct ath10k_snoc_qmi_config *)ind_cb_priv; struct ath10k_snoc *ar_snoc = container_of(qmi_cfg, struct ath10k_snoc, qmi_cfg); struct ath10k *ar = ar_snoc->ar; ath10k_dbg(ar, ATH10K_DBG_SNOC, "Received Ind 0x%x, msg_len: %d\n", msg_id, msg_len); switch (msg_id) { case QMI_WLFW_FW_READY_IND_V01: ath10k_snoc_driver_event_post( ATH10K_SNOC_DRIVER_EVENT_FW_READY_IND, 0, ar); break; case QMI_WLFW_MSA_READY_IND_V01: qmi_cfg->msa_ready = true; ath10k_dbg(ar, ATH10K_DBG_SNOC, "Received MSA Ready, ind = 0x%x\n", msg_id); break; default: ath10k_err(ar, "Invalid msg_id 0x%x\n", msg_id); break; } } static int ath10k_snoc_driver_event_server_arrive(struct ath10k *ar) { int ret = 0; struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); struct ath10k_snoc_qmi_config *qmi_cfg = &ar_snoc->qmi_cfg; if (!qmi_cfg) return -ENODEV; qmi_cfg->wlfw_clnt = qmi_handle_create( ath10k_snoc_qmi_wlfw_clnt_notify, qmi_cfg); if (!qmi_cfg->wlfw_clnt) { ath10k_dbg(ar, ATH10K_DBG_SNOC, "QMI client handle create failed\n"); return -ENOMEM; } ret = qmi_connect_to_service(qmi_cfg->wlfw_clnt, WLFW_SERVICE_ID_V01, WLFW_SERVICE_VERS_V01, WLFW_SERVICE_INS_ID_V01); if (ret < 0) { ath10k_err(ar, "QMI WLAN Service not found : %d\n", ret); goto err_qmi_config; } ret = qmi_register_ind_cb(qmi_cfg->wlfw_clnt, ath10k_snoc_qmi_wlfw_clnt_ind, qmi_cfg); if (ret < 0) { ath10k_err(ar, "Failed to register indication callback: %d\n", ret); goto err_qmi_config; } ret = ath10k_snoc_ind_register_send_sync_msg(ar); if (ret) { ath10k_err(ar, "Failed to config qmi ind register\n"); goto err_qmi_config; } atomic_set(&qmi_cfg->server_connected, 1); wake_up_all(&ath10k_fw_ready_wait_event); ath10k_dbg(ar, ATH10K_DBG_SNOC, "QMI Server Arrive Configuration Success\n"); return 0; err_qmi_config: qmi_handle_destroy(qmi_cfg->wlfw_clnt); qmi_cfg->wlfw_clnt = NULL; return ret; } static int ath10k_snoc_driver_event_server_exit(struct ath10k *ar) { struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); struct ath10k_snoc_qmi_config *qmi_cfg = &ar_snoc->qmi_cfg; ath10k_dbg(ar, ATH10K_DBG_SNOC, "QMI Server Exit event received\n"); atomic_set(&qmi_cfg->fw_ready, 0); qmi_cfg->msa_ready = false; atomic_set(&qmi_cfg->server_connected, 0); return 0; } static int ath10k_snoc_driver_event_fw_ready_ind(struct ath10k *ar) { struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); struct ath10k_snoc_qmi_config *qmi_cfg = &ar_snoc->qmi_cfg; ath10k_dbg(ar, ATH10K_DBG_SNOC, "FW Ready event received.\n"); atomic_set(&qmi_cfg->fw_ready, 1); wake_up_all(&ath10k_fw_ready_wait_event); return 0; } static void ath10k_snoc_driver_event_work(struct work_struct *work) { struct ath10k_snoc_qmi_driver_event *event; int ret; struct ath10k_snoc_qmi_config *qmi_cfg = container_of(work, struct ath10k_snoc_qmi_config, event_work); struct ath10k_snoc *ar_snoc = container_of(qmi_cfg, struct ath10k_snoc, qmi_cfg); struct ath10k *ar = ar_snoc->ar; spin_lock_bh(&qmi_cfg->event_lock); while (!list_empty(&qmi_cfg->event_list)) { event = list_first_entry(&qmi_cfg->event_list, struct ath10k_snoc_qmi_driver_event, list); list_del(&event->list); spin_unlock_bh(&qmi_cfg->event_lock); ath10k_dbg(ar, ATH10K_DBG_SNOC, "Processing event: %s%s(%d)\n", ath10k_snoc_driver_event_to_str(event->type), event->sync ? "-sync" : "", event->type); switch (event->type) { case ATH10K_SNOC_DRIVER_EVENT_SERVER_ARRIVE: ret = ath10k_snoc_driver_event_server_arrive(ar); break; case ATH10K_SNOC_DRIVER_EVENT_SERVER_EXIT: ret = ath10k_snoc_driver_event_server_exit(ar); break; case ATH10K_SNOC_DRIVER_EVENT_FW_READY_IND: ret = ath10k_snoc_driver_event_fw_ready_ind(ar); break; default: ath10k_err(ar, "Invalid Event type: %d", event->type); kfree(event); continue; } atomic_set(&event->event_handled, 1); ath10k_dbg(ar, ATH10K_DBG_SNOC, "Event Processed: %s%s(%d), ret: %d\n", ath10k_snoc_driver_event_to_str(event->type), event->sync ? "-sync" : "", event->type, ret); spin_lock_bh(&qmi_cfg->event_lock); if (event->sync) { event->ret = ret; complete(&event->complete); continue; } spin_unlock_bh(&qmi_cfg->event_lock); spin_lock_bh(&qmi_cfg->event_lock); } spin_unlock_bh(&qmi_cfg->event_lock); } static int ath10k_snoc_qmi_wlfw_clnt_svc_event_notify(struct notifier_block *this, unsigned long code, void *_cmd) { int ret = 0; struct ath10k_snoc_qmi_config *qmi_cfg = container_of(this, struct ath10k_snoc_qmi_config, wlfw_clnt_nb); struct ath10k_snoc *ar_snoc = container_of(qmi_cfg, struct ath10k_snoc, qmi_cfg); struct ath10k *ar = ar_snoc->ar; ath10k_dbg(ar, ATH10K_DBG_SNOC, "Event Notify: code: %ld", code); switch (code) { case QMI_SERVER_ARRIVE: ret = ath10k_snoc_driver_event_post( ATH10K_SNOC_DRIVER_EVENT_SERVER_ARRIVE, 0, ar); break; case QMI_SERVER_EXIT: ret = ath10k_snoc_driver_event_post( ATH10K_SNOC_DRIVER_EVENT_SERVER_EXIT, 0, ar); break; default: ath10k_err(ar, "Invalid code: %ld", code); break; } return ret; } int ath10k_snoc_start_qmi_service(struct ath10k *ar) { int ret; int i; struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); struct ath10k_snoc_qmi_config *qmi_cfg = &ar_snoc->qmi_cfg; qmi_cfg->event_wq = alloc_workqueue("ath10k_snoc_driver_event", WQ_UNBOUND, 1); if (!qmi_cfg->event_wq) { ath10k_err(ar, "Workqueue creation failed\n"); return -EFAULT; } spin_lock_init(&qmi_cfg->event_lock); atomic_set(&qmi_cfg->fw_ready, 0); atomic_set(&qmi_cfg->server_connected, 0); INIT_WORK(&qmi_cfg->event_work, ath10k_snoc_driver_event_work); INIT_WORK(&qmi_cfg->qmi_recv_msg_work, ath10k_snoc_qmi_wlfw_clnt_notify_work); INIT_LIST_HEAD(&qmi_cfg->event_list); for (i = 0; i < ATH10K_SNOC_DRIVER_EVENT_MAX; i++) atomic_set(&qmi_cfg->qmi_ev_list[i].event_handled, 1); qmi_cfg->wlfw_clnt_nb.notifier_call = ath10k_snoc_qmi_wlfw_clnt_svc_event_notify; ret = qmi_svc_event_notifier_register(WLFW_SERVICE_ID_V01, WLFW_SERVICE_VERS_V01, WLFW_SERVICE_INS_ID_V01, &qmi_cfg->wlfw_clnt_nb); if (ret < 0) { ath10k_err(ar, "Notifier register failed: %d\n", ret); ret = -EFAULT; goto out_destroy_wq; } atomic_set(&qmi_cfg->fw_ready, 1); ath10k_dbg(ar, ATH10K_DBG_SNOC, "QMI service started successfully\n"); return 0; out_destroy_wq: destroy_workqueue(qmi_cfg->event_wq); return ret; } void ath10k_snoc_stop_qmi_service(struct ath10k *ar) { struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); struct ath10k_snoc_qmi_config *qmi_cfg = &ar_snoc->qmi_cfg; ath10k_dbg(ar, ATH10K_DBG_SNOC, "Removing QMI service..\n"); qmi_svc_event_notifier_unregister(WLFW_SERVICE_ID_V01, WLFW_SERVICE_VERS_V01, WLFW_SERVICE_INS_ID_V01, &qmi_cfg->wlfw_clnt_nb); wake_up_all(&ath10k_fw_ready_wait_event); destroy_workqueue(qmi_cfg->event_wq); qmi_cfg = NULL; }
drivers/net/wireless/ath/ath10k/qmi.h +138 −1 Original line number Diff line number Diff line Loading @@ -11,9 +11,146 @@ */ #ifndef _QMI_H_ #define _QMI_H_ #define ATH10K_SNOC_EVENT_PENDING 2989 #define ATH10K_SNOC_EVENT_SYNC BIT(0) #define ATH10K_SNOC_EVENT_UNINTERRUPTIBLE BIT(1) #define ATH10K_SNOC_WLAN_FW_READY_TIMEOUT 8000 #define WLFW_SERVICE_INS_ID_V01 0 #define WLFW_CLIENT_ID 0x4b4e454c #define WLFW_TIMEOUT_MS 20000 enum ath10k_snoc_driver_event_type { ATH10K_SNOC_DRIVER_EVENT_SERVER_ARRIVE, ATH10K_SNOC_DRIVER_EVENT_SERVER_EXIT, ATH10K_SNOC_DRIVER_EVENT_FW_READY_IND, ATH10K_SNOC_DRIVER_EVENT_MAX, }; /* enum ath10k_driver_mode: ath10k driver mode * @ATH10K_MISSION: mission mode * @ATH10K_FTM: ftm mode * @ATH10K_EPPING: epping mode * @ATH10K_OFF: off mode */ enum ath10k_driver_mode { ATH10K_MISSION, ATH10K_FTM, ATH10K_EPPING, ATH10K_OFF }; /* struct ath10k_ce_tgt_pipe_cfg: target pipe configuration * @pipe_num: pipe number * @pipe_dir: pipe direction * @nentries: entries in pipe * @nbytes_max: pipe max size * @flags: pipe flags * @reserved: reserved */ struct ath10k_ce_tgt_pipe_cfg { u32 pipe_num; u32 pipe_dir; u32 nentries; u32 nbytes_max; u32 flags; u32 reserved; }; /* struct ath10k_ce_svc_pipe_cfg: service pipe configuration * @service_id: target version * @pipe_dir: pipe direction * @pipe_num: pipe number */ struct ath10k_ce_svc_pipe_cfg { u32 service_id; u32 pipe_dir; u32 pipe_num; }; /* struct ath10k_shadow_reg_cfg: shadow register configuration * @ce_id: copy engine id * @reg_offset: offset to copy engine */ struct ath10k_shadow_reg_cfg { u16 ce_id; u16 reg_offset; }; /* struct ath10k_wlan_enable_cfg: wlan enable configuration * @num_ce_tgt_cfg: no of ce target configuration * @ce_tgt_cfg: target ce configuration * @num_ce_svc_pipe_cfg: no of ce service configuration * @ce_svc_cfg: ce service configuration * @num_shadow_reg_cfg: no of shadow registers * @shadow_reg_cfg: shadow register configuration */ struct ath10k_wlan_enable_cfg { u32 num_ce_tgt_cfg; struct ath10k_ce_tgt_pipe_cfg *ce_tgt_cfg; u32 num_ce_svc_pipe_cfg; struct ath10k_ce_svc_pipe_cfg *ce_svc_cfg; u32 num_shadow_reg_cfg; struct ath10k_shadow_reg_cfg *shadow_reg_cfg; }; /* struct ath10k_snoc_qmi_driver_event: qmi driver event * event_handled: event handled by event work handler * sync: event synced * ret: event received return value * list: list to queue qmi event for process * type: driver event type * complete: completion for event handle complete * data: encapsulate driver data for event handler callback */ struct ath10k_snoc_qmi_driver_event { atomic_t event_handled; bool sync; int ret; struct list_head list; enum ath10k_snoc_driver_event_type type; struct completion complete; void *data; }; /* struct ath10k_snoc_qmi_config: qmi service configuration * fw_ready: wlan firmware ready for wlan operation * msa_ready: wlan firmware msa memory ready for board data download * server_connected: qmi server connected * event_work: QMI event work * event_list: QMI event list * qmi_recv_msg_work: QMI message receive work * event_wq: QMI event work queue * wlfw_clnt_nb: WLAN firmware indication callback * wlfw_clnt: QMI notifier handler for wlan firmware * qmi_ev_list: QMI event list * event_lock: spinlock for qmi event work queue */ struct ath10k_snoc_qmi_config { atomic_t fw_ready; bool msa_ready; atomic_t server_connected; struct work_struct event_work; struct list_head event_list; struct work_struct qmi_recv_msg_work; struct workqueue_struct *event_wq; struct notifier_block wlfw_clnt_nb; struct qmi_handle *wlfw_clnt; struct ath10k_snoc_qmi_driver_event qmi_ev_list[ATH10K_SNOC_DRIVER_EVENT_MAX]; spinlock_t event_lock; /* spinlock for qmi event work queue */ }; int ath10k_snoc_pd_restart_enable(struct ath10k *ar); int ath10k_snoc_modem_ssr_register_notifier(struct ath10k *ar); int ath10k_snoc_modem_ssr_unregister_notifier(struct ath10k *ar); int ath10k_snoc_pdr_unregister_notifier(struct ath10k *ar); int ath10k_snoc_start_qmi_service(struct ath10k *ar); void ath10k_snoc_stop_qmi_service(struct ath10k *ar); int ath10k_snoc_qmi_wlan_enable(struct ath10k *ar, struct ath10k_wlan_enable_cfg *config, enum ath10k_driver_mode mode, const char *host_version); int ath10k_snoc_qmi_wlan_disable(struct ath10k *ar); #endif
drivers/net/wireless/ath/ath10k/snoc.c +19 −15 File changed.Preview size limit exceeded, changes collapsed. Show changes
drivers/net/wireless/ath/ath10k/snoc.h +2 −67 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ #include "hw.h" #include "ce.h" #include "pci.h" #include "qmi.h" #include <soc/qcom/service-locator.h> #define ATH10K_SNOC_RX_POST_RETRY_MS 50 #define CE_POLL_PIPE 4 Loading Loading @@ -143,60 +144,7 @@ struct ath10k_snoc { int total_domains; struct notifier_block get_service_nb; atomic_t fw_crashed; }; /* struct ath10k_ce_tgt_pipe_cfg: target pipe configuration * @pipe_num: pipe number * @pipe_dir: pipe direction * @nentries: entries in pipe * @nbytes_max: pipe max size * @flags: pipe flags * @reserved: reserved */ struct ath10k_ce_tgt_pipe_cfg { u32 pipe_num; u32 pipe_dir; u32 nentries; u32 nbytes_max; u32 flags; u32 reserved; }; /* struct ath10k_ce_svc_pipe_cfg: service pipe configuration * @service_id: target version * @pipe_dir: pipe direction * @pipe_num: pipe number */ struct ath10k_ce_svc_pipe_cfg { u32 service_id; u32 pipe_dir; u32 pipe_num; }; /* struct ath10k_shadow_reg_cfg: shadow register configuration * @ce_id: copy engine id * @reg_offset: offset to copy engine */ struct ath10k_shadow_reg_cfg { u16 ce_id; u16 reg_offset; }; /* struct ath10k_wlan_enable_cfg: wlan enable configuration * @num_ce_tgt_cfg: no of ce target configuration * @ce_tgt_cfg: target ce configuration * @num_ce_svc_pipe_cfg: no of ce service configuration * @ce_svc_cfg: ce service configuration * @num_shadow_reg_cfg: no of shadow registers * @shadow_reg_cfg: shadow register configuration */ struct ath10k_wlan_enable_cfg { u32 num_ce_tgt_cfg; struct ath10k_ce_tgt_pipe_cfg *ce_tgt_cfg; u32 num_ce_svc_pipe_cfg; struct ath10k_ce_svc_pipe_cfg *ce_svc_cfg; u32 num_shadow_reg_cfg; struct ath10k_shadow_reg_cfg *shadow_reg_cfg; struct ath10k_snoc_qmi_config qmi_cfg; }; struct ath10k_event_pd_down_data { Loading @@ -204,19 +152,6 @@ struct ath10k_event_pd_down_data { bool fw_rejuvenate; }; /* enum ath10k_driver_mode: ath10k driver mode * @ATH10K_MISSION: mission mode * @ATH10K_FTM: ftm mode * @ATH10K_EPPING: epping mode * @ATH10K_OFF: off mode */ enum ath10k_driver_mode { ATH10K_MISSION, ATH10K_FTM, ATH10K_EPPING, ATH10K_OFF }; static inline struct ath10k_snoc *ath10k_snoc_priv(struct ath10k *ar) { return (struct ath10k_snoc *)ar->drv_priv; Loading