summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/net/wireless/ath/ath10k/qmi.c441
-rw-r--r--drivers/net/wireless/ath/ath10k/qmi.h132
-rw-r--r--drivers/net/wireless/ath/ath10k/snoc.c7
-rw-r--r--drivers/net/wireless/ath/ath10k/snoc.h69
4 files changed, 580 insertions, 69 deletions
diff --git a/drivers/net/wireless/ath/ath10k/qmi.c b/drivers/net/wireless/ath/ath10k/qmi.c
index 7b5fc52d269a..1d92965d671e 100644
--- a/drivers/net/wireless/ath/ath10k/qmi.c
+++ b/drivers/net/wireless/ath/ath10k/qmi.c
@@ -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,
@@ -228,3 +232,438 @@ 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_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;
+ }
+
+ 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);
+
+ ath10k_dbg(ar, ATH10K_DBG_SNOC, "QMI Server Exit event received\n");
+ ar_snoc->qmi_cfg.fw_ready = false;
+ ar_snoc->qmi_cfg.msa_ready = false;
+
+ return 0;
+}
+
+static int ath10k_snoc_driver_event_fw_ready_ind(struct ath10k *ar)
+{
+ struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+
+ ath10k_dbg(ar, ATH10K_DBG_SNOC, "FW Ready event received.\n");
+ ar_snoc->qmi_cfg.fw_ready = true;
+ 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);
+ qmi_cfg->fw_ready = false;
+
+ 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;
+ }
+
+ 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;
+}
diff --git a/drivers/net/wireless/ath/ath10k/qmi.h b/drivers/net/wireless/ath/ath10k/qmi.h
index f8ba3288753b..9305847e9ec4 100644
--- a/drivers/net/wireless/ath/ath10k/qmi.h
+++ b/drivers/net/wireless/ath/ath10k/qmi.h
@@ -11,9 +11,139 @@
*/
#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 6000
+
+#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
+ * 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 {
+ bool fw_ready;
+ bool msa_ready;
+ 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);
#endif
diff --git a/drivers/net/wireless/ath/ath10k/snoc.c b/drivers/net/wireless/ath/ath10k/snoc.c
index 89042dcf70a0..4358bc8abfd3 100644
--- a/drivers/net/wireless/ath/ath10k/snoc.c
+++ b/drivers/net/wireless/ath/ath10k/snoc.c
@@ -1245,6 +1245,12 @@ static int ath10k_snoc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, ar);
ar_snoc->ar = ar;
+ ret = ath10k_snoc_start_qmi_service(ar);
+ if (ret) {
+ ath10k_err(ar, "failed to start QMI service: %d\n", ret);
+ goto err_core_destroy;
+ }
+
spin_lock_init(&ar_snoc->opaque_ctx.ce_lock);
ar_snoc->opaque_ctx.bus_ops = &ath10k_snoc_bus_ops;
ath10k_snoc_resource_init(ar);
@@ -1325,6 +1331,7 @@ static int ath10k_snoc_remove(struct platform_device *pdev)
ath10k_snoc_free_irq(ar);
ath10k_snoc_release_resource(ar);
ath10k_snoc_free_pipes(ar);
+ ath10k_snoc_stop_qmi_service(ar);
ath10k_core_destroy(ar);
ath10k_dbg(ar, ATH10K_DBG_SNOC, "%s:WCN3990 removed\n", __func__);
diff --git a/drivers/net/wireless/ath/ath10k/snoc.h b/drivers/net/wireless/ath/ath10k/snoc.h
index c62519b2a340..99ae157885bb 100644
--- a/drivers/net/wireless/ath/ath10k/snoc.h
+++ b/drivers/net/wireless/ath/ath10k/snoc.h
@@ -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
@@ -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 {
@@ -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;