Loading drivers/soc/qcom/icnss2/main.c +266 −1 Original line number Diff line number Diff line Loading @@ -177,6 +177,20 @@ char *icnss_driver_event_to_str(enum icnss_driver_event_type type) return "UNKNOWN"; }; char *icnss_soc_wake_event_to_str(enum icnss_soc_wake_event_type type) { switch (type) { case ICNSS_SOC_WAKE_REQUEST_EVENT: return "SOC_WAKE_REQUEST"; case ICNSS_SOC_WAKE_RELEASE_EVENT: return "SOC_WAKE_RELEASE"; case ICNSS_SOC_WAKE_EVENT_MAX: return "SOC_EVENT_MAX"; } return "UNKNOWN"; }; int icnss_driver_event_post(struct icnss_priv *priv, enum icnss_driver_event_type type, u32 flags, void *data) Loading Loading @@ -249,6 +263,78 @@ int icnss_driver_event_post(struct icnss_priv *priv, return ret; } int icnss_soc_wake_event_post(struct icnss_priv *priv, enum icnss_soc_wake_event_type type, u32 flags, void *data) { struct icnss_soc_wake_event *event; unsigned long irq_flags; int gfp = GFP_KERNEL; int ret = 0; if (!priv) return -ENODEV; icnss_pr_dbg("Posting event: %s(%d), %s, flags: 0x%x, state: 0x%lx\n", icnss_soc_wake_event_to_str(type), type, current->comm, flags, priv->state); if (type >= ICNSS_SOC_WAKE_EVENT_MAX) { icnss_pr_err("Invalid Event type: %d, can't post", type); return -EINVAL; } if (in_interrupt() || irqs_disabled()) gfp = GFP_ATOMIC; event = kzalloc(sizeof(*event), gfp); if (!event) return -ENOMEM; icnss_pm_stay_awake(priv); event->type = type; event->data = data; init_completion(&event->complete); event->ret = ICNSS_EVENT_PENDING; event->sync = !!(flags & ICNSS_EVENT_SYNC); spin_lock_irqsave(&priv->soc_wake_msg_lock, irq_flags); list_add_tail(&event->list, &priv->soc_wake_msg_list); spin_unlock_irqrestore(&priv->soc_wake_msg_lock, irq_flags); priv->stats.soc_wake_events[type].posted++; queue_work(priv->soc_wake_wq, &priv->soc_wake_msg_work); if (!(flags & ICNSS_EVENT_SYNC)) goto out; if (flags & ICNSS_EVENT_UNINTERRUPTIBLE) wait_for_completion(&event->complete); else ret = wait_for_completion_interruptible(&event->complete); icnss_pr_dbg("Completed event: %s(%d), state: 0x%lx, ret: %d/%d\n", icnss_soc_wake_event_to_str(type), type, priv->state, ret, event->ret); spin_lock_irqsave(&priv->soc_wake_msg_lock, irq_flags); if (ret == -ERESTARTSYS && event->ret == ICNSS_EVENT_PENDING) { event->sync = false; spin_unlock_irqrestore(&priv->soc_wake_msg_lock, irq_flags); ret = -EINTR; goto out; } spin_unlock_irqrestore(&priv->soc_wake_msg_lock, irq_flags); ret = event->ret; kfree(event); out: icnss_pm_relax(priv); return ret; } bool icnss_is_fw_ready(void) { if (!penv) Loading Loading @@ -888,6 +974,41 @@ static int icnss_qdss_trace_save_hdlr(struct icnss_priv *priv, return ret; } static int icnss_event_soc_wake_request(struct icnss_priv *priv, void *data) { int ret = 0; if (!priv) return -ENODEV; ret = wlfw_send_soc_wake_msg(priv, QMI_WLFW_WAKE_REQUEST_V01); if (!ret) atomic_inc(&priv->soc_wake_ref_count); return ret; } static int icnss_event_soc_wake_release(struct icnss_priv *priv, void *data) { int ret = 0; int count = 0; if (!priv) return -ENODEV; count = atomic_dec_return(&priv->soc_wake_ref_count); if (count) { icnss_pr_dbg("Wake release not called. Ref count: %d", count); return 0; } ret = wlfw_send_soc_wake_msg(priv, QMI_WLFW_WAKE_RELEASE_V01); return ret; } static int icnss_driver_event_register_driver(struct icnss_priv *priv, void *data) { Loading Loading @@ -1225,6 +1346,68 @@ static void icnss_driver_event_work(struct work_struct *work) icnss_pm_relax(priv); } static void icnss_soc_wake_msg_work(struct work_struct *work) { struct icnss_priv *priv = container_of(work, struct icnss_priv, soc_wake_msg_work); struct icnss_soc_wake_event *event; unsigned long flags; int ret; icnss_pm_stay_awake(priv); spin_lock_irqsave(&priv->soc_wake_msg_lock, flags); while (!list_empty(&priv->soc_wake_msg_list)) { event = list_first_entry(&priv->soc_wake_msg_list, struct icnss_soc_wake_event, list); list_del(&event->list); spin_unlock_irqrestore(&priv->soc_wake_msg_lock, flags); icnss_pr_dbg("Processing event: %s%s(%d), state: 0x%lx\n", icnss_soc_wake_event_to_str(event->type), event->sync ? "-sync" : "", event->type, priv->state); switch (event->type) { case ICNSS_SOC_WAKE_REQUEST_EVENT: ret = icnss_event_soc_wake_request(priv, event->data); break; case ICNSS_SOC_WAKE_RELEASE_EVENT: ret = icnss_event_soc_wake_release(priv, event->data); break; default: icnss_pr_err("Invalid Event type: %d", event->type); kfree(event); continue; } priv->stats.soc_wake_events[event->type].processed++; icnss_pr_dbg("Event Processed: %s%s(%d), ret: %d, state: 0x%lx\n", icnss_soc_wake_event_to_str(event->type), event->sync ? "-sync" : "", event->type, ret, priv->state); spin_lock_irqsave(&priv->soc_wake_msg_lock, flags); if (event->sync) { event->ret = ret; complete(&event->complete); continue; } spin_unlock_irqrestore(&priv->soc_wake_msg_lock, flags); kfree(event); spin_lock_irqsave(&priv->soc_wake_msg_lock, flags); } spin_unlock_irqrestore(&priv->soc_wake_msg_lock, flags); icnss_pm_relax(priv); } static int icnss_msa0_ramdump(struct icnss_priv *priv) { struct ramdump_segment segment; Loading Loading @@ -1963,6 +2146,71 @@ int icnss_set_fw_log_mode(struct device *dev, uint8_t fw_log_mode) } EXPORT_SYMBOL(icnss_set_fw_log_mode); int icnss_force_wake_request(struct device *dev) { struct icnss_priv *priv = dev_get_drvdata(dev); int count = 0; if (!dev) return -ENODEV; if (!priv) { icnss_pr_err("Platform driver not initialized\n"); return -EINVAL; } icnss_pr_dbg("Calling SOC Wake request"); if (atomic_read(&priv->soc_wake_ref_count)) { count = atomic_inc_return(&priv->soc_wake_ref_count); icnss_pr_dbg("SOC already awake, Ref count: %d", count); return 0; } icnss_soc_wake_event_post(priv, ICNSS_SOC_WAKE_REQUEST_EVENT, 0, NULL); return 0; } EXPORT_SYMBOL(icnss_force_wake_request); int icnss_force_wake_release(struct device *dev) { struct icnss_priv *priv = dev_get_drvdata(dev); if (!dev) return -ENODEV; if (!priv) { icnss_pr_err("Platform driver not initialized\n"); return -EINVAL; } icnss_pr_dbg("Calling SOC Wake response"); icnss_soc_wake_event_post(priv, ICNSS_SOC_WAKE_RELEASE_EVENT, 0, NULL); return 0; } EXPORT_SYMBOL(icnss_force_wake_release); int icnss_is_device_awake(struct device *dev) { struct icnss_priv *priv = dev_get_drvdata(dev); if (!dev) return -ENODEV; if (!priv) { icnss_pr_err("Platform driver not initialized\n"); return -EINVAL; } return atomic_read(&priv->soc_wake_ref_count); } EXPORT_SYMBOL(icnss_is_device_awake); int icnss_athdiag_read(struct device *dev, uint32_t offset, uint32_t mem_type, uint32_t data_len, uint8_t *output) Loading Loading @@ -2656,6 +2904,7 @@ static int icnss_probe(struct platform_device *pdev) spin_lock_init(&priv->event_lock); spin_lock_init(&priv->on_off_lock); spin_lock_init(&priv->soc_wake_msg_lock); mutex_init(&priv->dev_lock); priv->event_wq = alloc_workqueue("icnss_driver_event", WQ_UNBOUND, 1); Loading @@ -2668,10 +2917,21 @@ static int icnss_probe(struct platform_device *pdev) INIT_WORK(&priv->event_work, icnss_driver_event_work); INIT_LIST_HEAD(&priv->event_list); priv->soc_wake_wq = alloc_workqueue("icnss_soc_wake_event", WQ_UNBOUND, 1); if (!priv->soc_wake_wq) { icnss_pr_err("Soc wake Workqueue creation failed\n"); ret = -EFAULT; goto out_destroy_wq; } INIT_WORK(&priv->soc_wake_msg_work, icnss_soc_wake_msg_work); INIT_LIST_HEAD(&priv->soc_wake_msg_list); ret = icnss_register_fw_service(priv); if (ret < 0) { icnss_pr_err("fw service registration failed: %d\n", ret); goto out_destroy_wq; goto out_destroy_soc_wq; } icnss_enable_recovery(priv); Loading @@ -2697,6 +2957,8 @@ static int icnss_probe(struct platform_device *pdev) return 0; out_destroy_soc_wq: destroy_workqueue(priv->soc_wake_wq); out_destroy_wq: destroy_workqueue(priv->event_wq); smmu_cleanup: Loading Loading @@ -2733,6 +2995,9 @@ static int icnss_remove(struct platform_device *pdev) if (priv->event_wq) destroy_workqueue(priv->event_wq); if (priv->soc_wake_wq) destroy_workqueue(priv->soc_wake_wq); priv->iommu_domain = NULL; icnss_hw_power_off(priv); Loading drivers/soc/qcom/icnss2/main.h +32 −0 Original line number Diff line number Diff line Loading @@ -55,6 +55,12 @@ enum icnss_driver_event_type { ICNSS_DRIVER_EVENT_MAX, }; enum icnss_soc_wake_event_type { ICNSS_SOC_WAKE_REQUEST_EVENT, ICNSS_SOC_WAKE_RELEASE_EVENT, ICNSS_SOC_WAKE_EVENT_MAX, }; struct icnss_event_server_arrive_data { unsigned int node; unsigned int port; Loading @@ -74,6 +80,15 @@ struct icnss_driver_event { void *data; }; struct icnss_soc_wake_event { struct list_head list; enum icnss_soc_wake_event_type type; bool sync; struct completion complete; int ret; void *data; }; enum icnss_driver_state { ICNSS_WLFW_CONNECTED, ICNSS_POWER_ON, Loading Loading @@ -149,6 +164,11 @@ struct icnss_stats { uint32_t processed; } events[ICNSS_DRIVER_EVENT_MAX]; struct { u32 posted; u32 processed; } soc_wake_events[ICNSS_SOC_WAKE_EVENT_MAX]; struct { uint32_t request; uint32_t free; Loading Loading @@ -210,6 +230,9 @@ struct icnss_stats { u32 exit_power_save_req; u32 exit_power_save_resp; u32 exit_power_save_err; u32 soc_wake_req; u32 soc_wake_resp; u32 soc_wake_err; }; #define WLFW_MAX_TIMESTAMP_LEN 32 Loading Loading @@ -282,10 +305,14 @@ struct icnss_priv { size_t smmu_iova_ipa_len; struct qmi_handle qmi; struct list_head event_list; struct list_head soc_wake_msg_list; spinlock_t event_lock; spinlock_t soc_wake_msg_lock; struct work_struct event_work; struct work_struct fw_recv_msg_work; struct work_struct soc_wake_msg_work; struct workqueue_struct *event_wq; struct workqueue_struct *soc_wake_wq; phys_addr_t msa_pa; phys_addr_t msi_addr_pa; dma_addr_t msi_addr_iova; Loading Loading @@ -342,6 +369,7 @@ struct icnss_priv { struct icnss_fw_mem qdss_mem[QMI_WLFW_MAX_NUM_MEM_SEG]; void *get_info_cb_ctx; int (*get_info_cb)(void *ctx, void *event, int event_len); atomic_t soc_wake_ref_count; }; struct icnss_reg_info { Loading @@ -358,5 +386,9 @@ int icnss_driver_event_post(struct icnss_priv *priv, u32 flags, void *data); void icnss_allow_recursive_recovery(struct device *dev); void icnss_disallow_recursive_recovery(struct device *dev); char *icnss_soc_wake_event_to_str(enum icnss_soc_wake_event_type type); int icnss_soc_wake_event_post(struct icnss_priv *priv, enum icnss_soc_wake_event_type type, u32 flags, void *data); #endif drivers/soc/qcom/icnss2/qmi.c +77 −1 Original line number Diff line number Diff line Loading @@ -413,6 +413,82 @@ int wlfw_exit_power_save_send_msg(struct icnss_priv *priv) return ret; } int wlfw_send_soc_wake_msg(struct icnss_priv *priv, enum wlfw_soc_wake_enum_v01 type) { int ret; struct wlfw_soc_wake_req_msg_v01 *req; struct wlfw_soc_wake_resp_msg_v01 *resp; struct qmi_txn txn; if (!priv) return -ENODEV; if (test_bit(ICNSS_FW_DOWN, &priv->state)) return -EINVAL; icnss_pr_dbg("Sending soc wake msg, type: 0x%x\n", type); req = kzalloc(sizeof(*req), GFP_KERNEL); if (!req) return -ENOMEM; resp = kzalloc(sizeof(*resp), GFP_KERNEL); if (!resp) { kfree(req); return -ENOMEM; } req->wake_valid = 1; req->wake = type; priv->stats.soc_wake_req++; ret = qmi_txn_init(&priv->qmi, &txn, wlfw_soc_wake_resp_msg_v01_ei, resp); if (ret < 0) { icnss_pr_err("Fail to init txn for wake msg resp %d\n", ret); goto out; } ret = qmi_send_request(&priv->qmi, NULL, &txn, QMI_WLFW_SOC_WAKE_REQ_V01, WLFW_SOC_WAKE_REQ_MSG_V01_MAX_MSG_LEN, wlfw_soc_wake_req_msg_v01_ei, req); if (ret < 0) { qmi_txn_cancel(&txn); icnss_pr_err("Fail to send soc wake msg %d\n", ret); goto out; } ret = qmi_txn_wait(&txn, priv->ctrl_params.qmi_timeout); if (ret < 0) { icnss_qmi_fatal_err("SOC wake timed out with ret %d\n", ret); goto out; } else if (resp->resp.result != QMI_RESULT_SUCCESS_V01) { icnss_qmi_fatal_err( "SOC wake request rejected,result:%d error:%d\n", resp->resp.result, resp->resp.error); ret = -resp->resp.result; goto out; } priv->stats.soc_wake_resp++; kfree(resp); kfree(req); return 0; out: kfree(req); kfree(resp); priv->stats.soc_wake_err++; return ret; } int wlfw_ind_register_send_sync_msg(struct icnss_priv *priv) { int ret; Loading Loading @@ -2196,7 +2272,7 @@ int icnss_wlfw_get_info_send_sync(struct icnss_priv *plat_priv, int type, if (cmd_len > QMI_WLFW_MAX_DATA_SIZE_V01) return -EINVAL; if (test_bit(ICNSS_FW_DOWN, &priv->state)) if (test_bit(ICNSS_FW_DOWN, &plat_priv->state)) return -EINVAL; req = kzalloc(sizeof(*req), GFP_KERNEL); Loading drivers/soc/qcom/icnss2/qmi.h +8 −0 Original line number Diff line number Diff line Loading @@ -139,6 +139,12 @@ int icnss_wlfw_get_info_send_sync(struct icnss_priv *priv, int type, { return 0; } int wlfw_send_soc_wake_msg(struct icnss_priv *priv, enum wlfw_soc_wake_enum_v01 type) { return 0; } #else int wlfw_ind_register_send_sync_msg(struct icnss_priv *priv); int icnss_connect_to_fw_server(struct icnss_priv *priv, void *data); Loading Loading @@ -177,6 +183,8 @@ int wlfw_qdss_trace_mem_info_send_sync(struct icnss_priv *priv); int wlfw_exit_power_save_send_msg(struct icnss_priv *priv); int icnss_wlfw_get_info_send_sync(struct icnss_priv *priv, int type, void *cmd, int cmd_len); int wlfw_send_soc_wake_msg(struct icnss_priv *priv, enum wlfw_soc_wake_enum_v01 type); #endif #endif /* __ICNSS_QMI_H__*/ include/soc/qcom/icnss2.h +3 −0 Original line number Diff line number Diff line Loading @@ -167,4 +167,7 @@ extern void icnss_get_msi_address(struct device *dev, u32 *msi_addr_low, extern int icnss_qmi_send(struct device *dev, int type, void *cmd, int cmd_len, void *cb_ctx, int (*cb)(void *ctx, void *event, int event_len)); extern int icnss_force_wake_request(struct device *dev); extern int icnss_force_wake_release(struct device *dev); extern int icnss_is_device_awake(struct device *dev); #endif /* _ICNSS_WLAN_H_ */ Loading
drivers/soc/qcom/icnss2/main.c +266 −1 Original line number Diff line number Diff line Loading @@ -177,6 +177,20 @@ char *icnss_driver_event_to_str(enum icnss_driver_event_type type) return "UNKNOWN"; }; char *icnss_soc_wake_event_to_str(enum icnss_soc_wake_event_type type) { switch (type) { case ICNSS_SOC_WAKE_REQUEST_EVENT: return "SOC_WAKE_REQUEST"; case ICNSS_SOC_WAKE_RELEASE_EVENT: return "SOC_WAKE_RELEASE"; case ICNSS_SOC_WAKE_EVENT_MAX: return "SOC_EVENT_MAX"; } return "UNKNOWN"; }; int icnss_driver_event_post(struct icnss_priv *priv, enum icnss_driver_event_type type, u32 flags, void *data) Loading Loading @@ -249,6 +263,78 @@ int icnss_driver_event_post(struct icnss_priv *priv, return ret; } int icnss_soc_wake_event_post(struct icnss_priv *priv, enum icnss_soc_wake_event_type type, u32 flags, void *data) { struct icnss_soc_wake_event *event; unsigned long irq_flags; int gfp = GFP_KERNEL; int ret = 0; if (!priv) return -ENODEV; icnss_pr_dbg("Posting event: %s(%d), %s, flags: 0x%x, state: 0x%lx\n", icnss_soc_wake_event_to_str(type), type, current->comm, flags, priv->state); if (type >= ICNSS_SOC_WAKE_EVENT_MAX) { icnss_pr_err("Invalid Event type: %d, can't post", type); return -EINVAL; } if (in_interrupt() || irqs_disabled()) gfp = GFP_ATOMIC; event = kzalloc(sizeof(*event), gfp); if (!event) return -ENOMEM; icnss_pm_stay_awake(priv); event->type = type; event->data = data; init_completion(&event->complete); event->ret = ICNSS_EVENT_PENDING; event->sync = !!(flags & ICNSS_EVENT_SYNC); spin_lock_irqsave(&priv->soc_wake_msg_lock, irq_flags); list_add_tail(&event->list, &priv->soc_wake_msg_list); spin_unlock_irqrestore(&priv->soc_wake_msg_lock, irq_flags); priv->stats.soc_wake_events[type].posted++; queue_work(priv->soc_wake_wq, &priv->soc_wake_msg_work); if (!(flags & ICNSS_EVENT_SYNC)) goto out; if (flags & ICNSS_EVENT_UNINTERRUPTIBLE) wait_for_completion(&event->complete); else ret = wait_for_completion_interruptible(&event->complete); icnss_pr_dbg("Completed event: %s(%d), state: 0x%lx, ret: %d/%d\n", icnss_soc_wake_event_to_str(type), type, priv->state, ret, event->ret); spin_lock_irqsave(&priv->soc_wake_msg_lock, irq_flags); if (ret == -ERESTARTSYS && event->ret == ICNSS_EVENT_PENDING) { event->sync = false; spin_unlock_irqrestore(&priv->soc_wake_msg_lock, irq_flags); ret = -EINTR; goto out; } spin_unlock_irqrestore(&priv->soc_wake_msg_lock, irq_flags); ret = event->ret; kfree(event); out: icnss_pm_relax(priv); return ret; } bool icnss_is_fw_ready(void) { if (!penv) Loading Loading @@ -888,6 +974,41 @@ static int icnss_qdss_trace_save_hdlr(struct icnss_priv *priv, return ret; } static int icnss_event_soc_wake_request(struct icnss_priv *priv, void *data) { int ret = 0; if (!priv) return -ENODEV; ret = wlfw_send_soc_wake_msg(priv, QMI_WLFW_WAKE_REQUEST_V01); if (!ret) atomic_inc(&priv->soc_wake_ref_count); return ret; } static int icnss_event_soc_wake_release(struct icnss_priv *priv, void *data) { int ret = 0; int count = 0; if (!priv) return -ENODEV; count = atomic_dec_return(&priv->soc_wake_ref_count); if (count) { icnss_pr_dbg("Wake release not called. Ref count: %d", count); return 0; } ret = wlfw_send_soc_wake_msg(priv, QMI_WLFW_WAKE_RELEASE_V01); return ret; } static int icnss_driver_event_register_driver(struct icnss_priv *priv, void *data) { Loading Loading @@ -1225,6 +1346,68 @@ static void icnss_driver_event_work(struct work_struct *work) icnss_pm_relax(priv); } static void icnss_soc_wake_msg_work(struct work_struct *work) { struct icnss_priv *priv = container_of(work, struct icnss_priv, soc_wake_msg_work); struct icnss_soc_wake_event *event; unsigned long flags; int ret; icnss_pm_stay_awake(priv); spin_lock_irqsave(&priv->soc_wake_msg_lock, flags); while (!list_empty(&priv->soc_wake_msg_list)) { event = list_first_entry(&priv->soc_wake_msg_list, struct icnss_soc_wake_event, list); list_del(&event->list); spin_unlock_irqrestore(&priv->soc_wake_msg_lock, flags); icnss_pr_dbg("Processing event: %s%s(%d), state: 0x%lx\n", icnss_soc_wake_event_to_str(event->type), event->sync ? "-sync" : "", event->type, priv->state); switch (event->type) { case ICNSS_SOC_WAKE_REQUEST_EVENT: ret = icnss_event_soc_wake_request(priv, event->data); break; case ICNSS_SOC_WAKE_RELEASE_EVENT: ret = icnss_event_soc_wake_release(priv, event->data); break; default: icnss_pr_err("Invalid Event type: %d", event->type); kfree(event); continue; } priv->stats.soc_wake_events[event->type].processed++; icnss_pr_dbg("Event Processed: %s%s(%d), ret: %d, state: 0x%lx\n", icnss_soc_wake_event_to_str(event->type), event->sync ? "-sync" : "", event->type, ret, priv->state); spin_lock_irqsave(&priv->soc_wake_msg_lock, flags); if (event->sync) { event->ret = ret; complete(&event->complete); continue; } spin_unlock_irqrestore(&priv->soc_wake_msg_lock, flags); kfree(event); spin_lock_irqsave(&priv->soc_wake_msg_lock, flags); } spin_unlock_irqrestore(&priv->soc_wake_msg_lock, flags); icnss_pm_relax(priv); } static int icnss_msa0_ramdump(struct icnss_priv *priv) { struct ramdump_segment segment; Loading Loading @@ -1963,6 +2146,71 @@ int icnss_set_fw_log_mode(struct device *dev, uint8_t fw_log_mode) } EXPORT_SYMBOL(icnss_set_fw_log_mode); int icnss_force_wake_request(struct device *dev) { struct icnss_priv *priv = dev_get_drvdata(dev); int count = 0; if (!dev) return -ENODEV; if (!priv) { icnss_pr_err("Platform driver not initialized\n"); return -EINVAL; } icnss_pr_dbg("Calling SOC Wake request"); if (atomic_read(&priv->soc_wake_ref_count)) { count = atomic_inc_return(&priv->soc_wake_ref_count); icnss_pr_dbg("SOC already awake, Ref count: %d", count); return 0; } icnss_soc_wake_event_post(priv, ICNSS_SOC_WAKE_REQUEST_EVENT, 0, NULL); return 0; } EXPORT_SYMBOL(icnss_force_wake_request); int icnss_force_wake_release(struct device *dev) { struct icnss_priv *priv = dev_get_drvdata(dev); if (!dev) return -ENODEV; if (!priv) { icnss_pr_err("Platform driver not initialized\n"); return -EINVAL; } icnss_pr_dbg("Calling SOC Wake response"); icnss_soc_wake_event_post(priv, ICNSS_SOC_WAKE_RELEASE_EVENT, 0, NULL); return 0; } EXPORT_SYMBOL(icnss_force_wake_release); int icnss_is_device_awake(struct device *dev) { struct icnss_priv *priv = dev_get_drvdata(dev); if (!dev) return -ENODEV; if (!priv) { icnss_pr_err("Platform driver not initialized\n"); return -EINVAL; } return atomic_read(&priv->soc_wake_ref_count); } EXPORT_SYMBOL(icnss_is_device_awake); int icnss_athdiag_read(struct device *dev, uint32_t offset, uint32_t mem_type, uint32_t data_len, uint8_t *output) Loading Loading @@ -2656,6 +2904,7 @@ static int icnss_probe(struct platform_device *pdev) spin_lock_init(&priv->event_lock); spin_lock_init(&priv->on_off_lock); spin_lock_init(&priv->soc_wake_msg_lock); mutex_init(&priv->dev_lock); priv->event_wq = alloc_workqueue("icnss_driver_event", WQ_UNBOUND, 1); Loading @@ -2668,10 +2917,21 @@ static int icnss_probe(struct platform_device *pdev) INIT_WORK(&priv->event_work, icnss_driver_event_work); INIT_LIST_HEAD(&priv->event_list); priv->soc_wake_wq = alloc_workqueue("icnss_soc_wake_event", WQ_UNBOUND, 1); if (!priv->soc_wake_wq) { icnss_pr_err("Soc wake Workqueue creation failed\n"); ret = -EFAULT; goto out_destroy_wq; } INIT_WORK(&priv->soc_wake_msg_work, icnss_soc_wake_msg_work); INIT_LIST_HEAD(&priv->soc_wake_msg_list); ret = icnss_register_fw_service(priv); if (ret < 0) { icnss_pr_err("fw service registration failed: %d\n", ret); goto out_destroy_wq; goto out_destroy_soc_wq; } icnss_enable_recovery(priv); Loading @@ -2697,6 +2957,8 @@ static int icnss_probe(struct platform_device *pdev) return 0; out_destroy_soc_wq: destroy_workqueue(priv->soc_wake_wq); out_destroy_wq: destroy_workqueue(priv->event_wq); smmu_cleanup: Loading Loading @@ -2733,6 +2995,9 @@ static int icnss_remove(struct platform_device *pdev) if (priv->event_wq) destroy_workqueue(priv->event_wq); if (priv->soc_wake_wq) destroy_workqueue(priv->soc_wake_wq); priv->iommu_domain = NULL; icnss_hw_power_off(priv); Loading
drivers/soc/qcom/icnss2/main.h +32 −0 Original line number Diff line number Diff line Loading @@ -55,6 +55,12 @@ enum icnss_driver_event_type { ICNSS_DRIVER_EVENT_MAX, }; enum icnss_soc_wake_event_type { ICNSS_SOC_WAKE_REQUEST_EVENT, ICNSS_SOC_WAKE_RELEASE_EVENT, ICNSS_SOC_WAKE_EVENT_MAX, }; struct icnss_event_server_arrive_data { unsigned int node; unsigned int port; Loading @@ -74,6 +80,15 @@ struct icnss_driver_event { void *data; }; struct icnss_soc_wake_event { struct list_head list; enum icnss_soc_wake_event_type type; bool sync; struct completion complete; int ret; void *data; }; enum icnss_driver_state { ICNSS_WLFW_CONNECTED, ICNSS_POWER_ON, Loading Loading @@ -149,6 +164,11 @@ struct icnss_stats { uint32_t processed; } events[ICNSS_DRIVER_EVENT_MAX]; struct { u32 posted; u32 processed; } soc_wake_events[ICNSS_SOC_WAKE_EVENT_MAX]; struct { uint32_t request; uint32_t free; Loading Loading @@ -210,6 +230,9 @@ struct icnss_stats { u32 exit_power_save_req; u32 exit_power_save_resp; u32 exit_power_save_err; u32 soc_wake_req; u32 soc_wake_resp; u32 soc_wake_err; }; #define WLFW_MAX_TIMESTAMP_LEN 32 Loading Loading @@ -282,10 +305,14 @@ struct icnss_priv { size_t smmu_iova_ipa_len; struct qmi_handle qmi; struct list_head event_list; struct list_head soc_wake_msg_list; spinlock_t event_lock; spinlock_t soc_wake_msg_lock; struct work_struct event_work; struct work_struct fw_recv_msg_work; struct work_struct soc_wake_msg_work; struct workqueue_struct *event_wq; struct workqueue_struct *soc_wake_wq; phys_addr_t msa_pa; phys_addr_t msi_addr_pa; dma_addr_t msi_addr_iova; Loading Loading @@ -342,6 +369,7 @@ struct icnss_priv { struct icnss_fw_mem qdss_mem[QMI_WLFW_MAX_NUM_MEM_SEG]; void *get_info_cb_ctx; int (*get_info_cb)(void *ctx, void *event, int event_len); atomic_t soc_wake_ref_count; }; struct icnss_reg_info { Loading @@ -358,5 +386,9 @@ int icnss_driver_event_post(struct icnss_priv *priv, u32 flags, void *data); void icnss_allow_recursive_recovery(struct device *dev); void icnss_disallow_recursive_recovery(struct device *dev); char *icnss_soc_wake_event_to_str(enum icnss_soc_wake_event_type type); int icnss_soc_wake_event_post(struct icnss_priv *priv, enum icnss_soc_wake_event_type type, u32 flags, void *data); #endif
drivers/soc/qcom/icnss2/qmi.c +77 −1 Original line number Diff line number Diff line Loading @@ -413,6 +413,82 @@ int wlfw_exit_power_save_send_msg(struct icnss_priv *priv) return ret; } int wlfw_send_soc_wake_msg(struct icnss_priv *priv, enum wlfw_soc_wake_enum_v01 type) { int ret; struct wlfw_soc_wake_req_msg_v01 *req; struct wlfw_soc_wake_resp_msg_v01 *resp; struct qmi_txn txn; if (!priv) return -ENODEV; if (test_bit(ICNSS_FW_DOWN, &priv->state)) return -EINVAL; icnss_pr_dbg("Sending soc wake msg, type: 0x%x\n", type); req = kzalloc(sizeof(*req), GFP_KERNEL); if (!req) return -ENOMEM; resp = kzalloc(sizeof(*resp), GFP_KERNEL); if (!resp) { kfree(req); return -ENOMEM; } req->wake_valid = 1; req->wake = type; priv->stats.soc_wake_req++; ret = qmi_txn_init(&priv->qmi, &txn, wlfw_soc_wake_resp_msg_v01_ei, resp); if (ret < 0) { icnss_pr_err("Fail to init txn for wake msg resp %d\n", ret); goto out; } ret = qmi_send_request(&priv->qmi, NULL, &txn, QMI_WLFW_SOC_WAKE_REQ_V01, WLFW_SOC_WAKE_REQ_MSG_V01_MAX_MSG_LEN, wlfw_soc_wake_req_msg_v01_ei, req); if (ret < 0) { qmi_txn_cancel(&txn); icnss_pr_err("Fail to send soc wake msg %d\n", ret); goto out; } ret = qmi_txn_wait(&txn, priv->ctrl_params.qmi_timeout); if (ret < 0) { icnss_qmi_fatal_err("SOC wake timed out with ret %d\n", ret); goto out; } else if (resp->resp.result != QMI_RESULT_SUCCESS_V01) { icnss_qmi_fatal_err( "SOC wake request rejected,result:%d error:%d\n", resp->resp.result, resp->resp.error); ret = -resp->resp.result; goto out; } priv->stats.soc_wake_resp++; kfree(resp); kfree(req); return 0; out: kfree(req); kfree(resp); priv->stats.soc_wake_err++; return ret; } int wlfw_ind_register_send_sync_msg(struct icnss_priv *priv) { int ret; Loading Loading @@ -2196,7 +2272,7 @@ int icnss_wlfw_get_info_send_sync(struct icnss_priv *plat_priv, int type, if (cmd_len > QMI_WLFW_MAX_DATA_SIZE_V01) return -EINVAL; if (test_bit(ICNSS_FW_DOWN, &priv->state)) if (test_bit(ICNSS_FW_DOWN, &plat_priv->state)) return -EINVAL; req = kzalloc(sizeof(*req), GFP_KERNEL); Loading
drivers/soc/qcom/icnss2/qmi.h +8 −0 Original line number Diff line number Diff line Loading @@ -139,6 +139,12 @@ int icnss_wlfw_get_info_send_sync(struct icnss_priv *priv, int type, { return 0; } int wlfw_send_soc_wake_msg(struct icnss_priv *priv, enum wlfw_soc_wake_enum_v01 type) { return 0; } #else int wlfw_ind_register_send_sync_msg(struct icnss_priv *priv); int icnss_connect_to_fw_server(struct icnss_priv *priv, void *data); Loading Loading @@ -177,6 +183,8 @@ int wlfw_qdss_trace_mem_info_send_sync(struct icnss_priv *priv); int wlfw_exit_power_save_send_msg(struct icnss_priv *priv); int icnss_wlfw_get_info_send_sync(struct icnss_priv *priv, int type, void *cmd, int cmd_len); int wlfw_send_soc_wake_msg(struct icnss_priv *priv, enum wlfw_soc_wake_enum_v01 type); #endif #endif /* __ICNSS_QMI_H__*/
include/soc/qcom/icnss2.h +3 −0 Original line number Diff line number Diff line Loading @@ -167,4 +167,7 @@ extern void icnss_get_msi_address(struct device *dev, u32 *msi_addr_low, extern int icnss_qmi_send(struct device *dev, int type, void *cmd, int cmd_len, void *cb_ctx, int (*cb)(void *ctx, void *event, int event_len)); extern int icnss_force_wake_request(struct device *dev); extern int icnss_force_wake_release(struct device *dev); extern int icnss_is_device_awake(struct device *dev); #endif /* _ICNSS_WLAN_H_ */