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

Commit 10841554 authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "icnss2: Add platform driver code for SOC Wake"

parents 8c0e61db c2024c4f
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_ */