Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c2024c4f authored by Mohammed Siddiq's avatar Mohammed Siddiq
Browse files

icnss2: Add platform driver code for SOC Wake



Add platform driver code changes to send soc wake request,
wake release and power save exit commands. Add work queue
to break context as qmi req has mutex which should not
be called in soft_irq context.

Change-Id: Ifba9f67cde8b99e0a5f3f283f5b454d4996d0011
Signed-off-by: default avatarMohammed Siddiq <msiddiq@codeaurora.org>
parent 5b863f1f
Loading
Loading
Loading
Loading
+266 −1
Original line number Diff line number Diff line
@@ -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)
@@ -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)
@@ -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)
{
@@ -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;
@@ -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)
@@ -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);
@@ -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);
@@ -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:
@@ -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);
+32 −0
Original line number Diff line number Diff line
@@ -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;
@@ -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,
@@ -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;
@@ -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
@@ -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;
@@ -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 {
@@ -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
+77 −1
Original line number Diff line number Diff line
@@ -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;
@@ -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);
+8 −0
Original line number Diff line number Diff line
@@ -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);
@@ -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__*/
+3 −0
Original line number Diff line number Diff line
@@ -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_ */