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

Commit a4bfae1e authored by Sagar Dharia's avatar Sagar Dharia Committed by Karthikeyan Ramasubramanian
Browse files

slim: msm: Support deferred response during system suspend



If runtime-pm does not get a chance to enter low-power mode before
system suspend is called, system suspend may need 2 passes since
power-off request needs to be sent to audio-DSP using QMI, and QMI
acquires wakelock if response is expected right-away.

Use Deferred-response during suspend to avoid 2 passes, and inquire
the power-off status when system resumes in that case.

CRs-Fixed: 2030850
Change-Id: I74fbb566186ed693f1d244e101f9205af48c3652
Signed-off-by: default avatarSagar Dharia <sdharia@codeaurora.org>
Signed-off-by: default avatarKarthikeyan Ramasubramanian <kramasub@codeaurora.org>
parent 68dfac8e
Loading
Loading
Loading
Loading
+21 −5
Original line number Diff line number Diff line
@@ -223,6 +223,7 @@ static int dsp_domr_notify_cb(struct notifier_block *n, unsigned long code,
		/* make sure autosuspend is not called until ADSP comes up*/
		pm_runtime_get_noresume(dev->dev);
		dev->state = MSM_CTRL_DOWN;
		dev->qmi.deferred_resp = false;
		msm_slim_sps_exit(dev, false);
		ngd_dom_down(dev);
		mutex_unlock(&dev->tx_lock);
@@ -2019,19 +2020,18 @@ static int ngd_slim_suspend(struct device *dev)
	if (!pm_runtime_enabled(dev) ||
		(!pm_runtime_suspended(dev) &&
			cdev->state == MSM_CTRL_IDLE)) {
		cdev->qmi.deferred_resp = true;
		ret = ngd_slim_runtime_suspend(dev);
		/*
		 * If runtime-PM still thinks it's active, then make sure its
		 * status is in sync with HW status.
		 * Since this suspend calls QMI api, it results in holding a
		 * wakelock. That results in failure of first suspend.
		 * Subsequent suspend should not call low-power transition
		 * again since the HW is already in suspended state.
		 */
		if (!ret) {
			pm_runtime_disable(dev);
			pm_runtime_set_suspended(dev);
			pm_runtime_enable(dev);
		} else {
			cdev->qmi.deferred_resp = false;
		}
	}
	if (ret == -EBUSY) {
@@ -2053,13 +2053,29 @@ static int ngd_slim_resume(struct device *dev)
{
	struct platform_device *pdev = to_platform_device(dev);
	struct msm_slim_ctrl *cdev = platform_get_drvdata(pdev);
	int ret = 0;

	/*
	 * If deferred response was requested for power-off and it failed,
	 * mark runtime-pm status as active to be consistent
	 * with HW status
	 */
	if (cdev->qmi.deferred_resp) {
		ret = msm_slim_qmi_deferred_status_req(cdev);
		if (ret) {
			pm_runtime_disable(dev);
			pm_runtime_set_active(dev);
			pm_runtime_enable(dev);
		}
		cdev->qmi.deferred_resp = false;
	}
	/*
	 * Rely on runtime-PM to call resume in case it is enabled.
	 * Even if it's not enabled, rely on 1st client transaction to do
	 * clock/power on
	 */
	SLIM_INFO(cdev, "system resume\n");
	return 0;
	return ret;
}
#endif /* CONFIG_PM_SLEEP */

+141 −12
Original line number Diff line number Diff line
@@ -1224,12 +1224,16 @@ void msm_slim_sps_exit(struct msm_slim_ctrl *dev, bool dereg)
#define SLIMBUS_QMI_POWER_RESP_V01 0x0021
#define SLIMBUS_QMI_CHECK_FRAMER_STATUS_REQ 0x0022
#define SLIMBUS_QMI_CHECK_FRAMER_STATUS_RESP 0x0022
#define SLIMBUS_QMI_DEFERRED_STATUS_REQ 0x0023
#define SLIMBUS_QMI_DEFERRED_STATUS_RESP 0x0023

#define SLIMBUS_QMI_POWER_REQ_MAX_MSG_LEN 7
#define SLIMBUS_QMI_POWER_REQ_MAX_MSG_LEN 14
#define SLIMBUS_QMI_POWER_RESP_MAX_MSG_LEN 7
#define SLIMBUS_QMI_SELECT_INSTANCE_REQ_MAX_MSG_LEN 14
#define SLIMBUS_QMI_SELECT_INSTANCE_RESP_MAX_MSG_LEN 7
#define SLIMBUS_QMI_CHECK_FRAMER_STAT_RESP_MAX_MSG_LEN 7
#define SLIMBUS_QMI_DEFERRED_STATUS_REQ_MSG_MAX_MSG_LEN 0
#define SLIMBUS_QMI_DEFERRED_STATUS_RESP_STAT_MSG_MAX_MSG_LEN 7

enum slimbus_mode_enum_type_v01 {
	/* To force a 32 bit signed enum. Do not change or use*/
@@ -1247,6 +1251,13 @@ enum slimbus_pm_enum_type_v01 {
	SLIMBUS_PM_ENUM_TYPE_MAX_ENUM_VAL_V01 = INT_MAX,
};

enum slimbus_resp_enum_type_v01 {
	SLIMBUS_RESP_ENUM_TYPE_MIN_VAL_V01 = INT_MIN,
	SLIMBUS_RESP_SYNCHRONOUS_V01 = 1,
	SLIMBUS_RESP_DEFERRED_V01 = 2,
	SLIMBUS_RESP_ENUM_TYPE_MAX_VAL_V01 = INT_MAX,
};

struct slimbus_select_inst_req_msg_v01 {
	/* Mandatory */
	/* Hardware Instance Selection */
@@ -1269,6 +1280,12 @@ struct slimbus_power_req_msg_v01 {
	/* Mandatory */
	/* Power Request Operation */
	enum slimbus_pm_enum_type_v01 pm_req;

	/* Optional */
	/* Optional Deferred Response type Operation */
	/* Must be set to true if type is being passed */
	uint8_t resp_type_valid;
	enum slimbus_resp_enum_type_v01 resp_type;
};

struct slimbus_power_resp_msg_v01 {
@@ -1283,6 +1300,9 @@ struct slimbus_chkfrm_resp_msg {
	struct qmi_response_type_v01 resp;
};

struct slimbus_deferred_status_resp {
	struct qmi_response_type_v01 resp;
};

static struct elem_info slimbus_select_inst_req_msg_v01_ei[] = {
	{
@@ -1358,6 +1378,24 @@ static struct elem_info slimbus_power_req_msg_v01_ei[] = {
		.offset    = offsetof(struct slimbus_power_req_msg_v01, pm_req),
		.ei_array  = NULL,
	},
	{
		.data_type      = QMI_OPT_FLAG,
		.elem_len       = 1,
		.elem_size      = sizeof(uint8_t),
		.is_array       = NO_ARRAY,
		.tlv_type       = 0x10,
		.offset         = offsetof(struct slimbus_power_req_msg_v01,
					   resp_type_valid),
	},
	{
		.data_type      = QMI_SIGNED_4_BYTE_ENUM,
		.elem_len       = 1,
		.elem_size      = sizeof(enum slimbus_resp_enum_type_v01),
		.is_array       = NO_ARRAY,
		.tlv_type       = 0x10,
		.offset         = offsetof(struct slimbus_power_req_msg_v01,
					   resp_type),
	},
	{
		.data_type = QMI_EOTI,
		.elem_len  = 0,
@@ -1411,6 +1449,22 @@ static struct elem_info slimbus_chkfrm_resp_msg_v01_ei[] = {
	},
};

static struct elem_info slimbus_deferred_status_resp_msg_v01_ei[] = {
	{
		.data_type      = QMI_STRUCT,
		.elem_len       = 1,
		.elem_size      = sizeof(struct qmi_response_type_v01),
		.is_array       = NO_ARRAY,
		.tlv_type       = 0x02,
		.offset         = offsetof(struct slimbus_deferred_status_resp,
					   resp),
		.ei_array      = get_qmi_response_type_v01_ei(),
	},
	{
		.data_type      = QMI_EOTI,
		.is_array       = NO_ARRAY,
	},
};
static void msm_slim_qmi_recv_msg(struct kthread_work *work)
{
	int rc;
@@ -1488,32 +1542,56 @@ static int msm_slim_qmi_send_select_inst_req(struct msm_slim_ctrl *dev,
	return 0;
}

static void slim_qmi_resp_cb(struct qmi_handle *handle, unsigned int msg_id,
			     void *msg, void *resp_cb_data, int stat)
{
	struct slimbus_power_resp_msg_v01 *resp = msg;
	struct msm_slim_ctrl *dev = resp_cb_data;

	if (msg_id != SLIMBUS_QMI_POWER_RESP_V01)
		SLIM_WARN(dev, "incorrect msg id in qmi-resp CB:0x%x", msg_id);
	else if (resp->resp.result != QMI_RESULT_SUCCESS_V01)
		SLIM_ERR(dev, "%s: QMI power failed 0x%x (%s)\n", __func__,
			 resp->resp.result, get_qmi_error(&resp->resp));

	complete(&dev->qmi.defer_comp);
}

static int msm_slim_qmi_send_power_request(struct msm_slim_ctrl *dev,
				struct slimbus_power_req_msg_v01 *req)
{
	struct slimbus_power_resp_msg_v01 resp = { { 0, 0 } };
	struct msg_desc req_desc, resp_desc;
	struct slimbus_power_resp_msg_v01 *resp =
		(struct slimbus_power_resp_msg_v01 *)&dev->qmi.resp;
	struct msg_desc req_desc;
	struct msg_desc *resp_desc = &dev->qmi.resp_desc;
	int rc;

	req_desc.msg_id = SLIMBUS_QMI_POWER_REQ_V01;
	req_desc.max_msg_len = SLIMBUS_QMI_POWER_REQ_MAX_MSG_LEN;
	req_desc.ei_array = slimbus_power_req_msg_v01_ei;

	resp_desc.msg_id = SLIMBUS_QMI_POWER_RESP_V01;
	resp_desc.max_msg_len = SLIMBUS_QMI_POWER_RESP_MAX_MSG_LEN;
	resp_desc.ei_array = slimbus_power_resp_msg_v01_ei;
	resp_desc->msg_id = SLIMBUS_QMI_POWER_RESP_V01;
	resp_desc->max_msg_len = SLIMBUS_QMI_POWER_RESP_MAX_MSG_LEN;
	resp_desc->ei_array = slimbus_power_resp_msg_v01_ei;

	rc = qmi_send_req_wait(dev->qmi.handle, &req_desc, req, sizeof(*req),
			&resp_desc, &resp, sizeof(resp), SLIM_QMI_RESP_TOUT);
	if (rc < 0) {
	if (dev->qmi.deferred_resp)
		rc = qmi_send_req_nowait(dev->qmi.handle, &req_desc, req,
				       sizeof(*req), resp_desc, resp,
				       sizeof(*resp), slim_qmi_resp_cb, dev);
	else
		rc = qmi_send_req_wait(dev->qmi.handle, &req_desc, req,
				       sizeof(*req), resp_desc, resp,
				       sizeof(*resp), SLIM_QMI_RESP_TOUT);
	if (rc < 0)
		SLIM_ERR(dev, "%s: QMI send req failed %d\n", __func__, rc);

	if (rc < 0 || dev->qmi.deferred_resp)
		return rc;
	}

	/* Check the response */
	if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
	if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
		SLIM_ERR(dev, "%s: QMI request failed 0x%x (%s)\n", __func__,
				resp.resp.result, get_qmi_error(&resp.resp));
				resp->resp.result, get_qmi_error(&resp->resp));
		return -EREMOTEIO;
	}

@@ -1527,6 +1605,7 @@ int msm_slim_qmi_init(struct msm_slim_ctrl *dev, bool apps_is_master)
	struct slimbus_select_inst_req_msg_v01 req;

	kthread_init_worker(&dev->qmi.kworker);
	init_completion(&dev->qmi.defer_comp);

	dev->qmi.task = kthread_run(kthread_worker_fn,
			&dev->qmi.kworker, "msm_slim_qmi_clnt%d", dev->ctrl.nr);
@@ -1604,6 +1683,13 @@ int msm_slim_qmi_power_request(struct msm_slim_ctrl *dev, bool active)
	else
		req.pm_req = SLIMBUS_PM_INACTIVE_V01;

	if (dev->qmi.deferred_resp) {
		req.resp_type = SLIMBUS_RESP_DEFERRED_V01;
		req.resp_type_valid = 1;
	} else {
		req.resp_type_valid = 0;
	}

	return msm_slim_qmi_send_power_request(dev, &req);
}

@@ -1635,3 +1721,46 @@ int msm_slim_qmi_check_framer_request(struct msm_slim_ctrl *dev)
	}
	return 0;
}

int msm_slim_qmi_deferred_status_req(struct msm_slim_ctrl *dev)
{
	struct slimbus_deferred_status_resp resp = { { 0, 0 } };
	struct msg_desc req_desc, resp_desc;
	int rc;

	req_desc.msg_id = SLIMBUS_QMI_DEFERRED_STATUS_REQ;
	req_desc.max_msg_len = 0;
	req_desc.ei_array = NULL;

	resp_desc.msg_id = SLIMBUS_QMI_DEFERRED_STATUS_RESP;
	resp_desc.max_msg_len =
		SLIMBUS_QMI_DEFERRED_STATUS_RESP_STAT_MSG_MAX_MSG_LEN;
	resp_desc.ei_array = slimbus_deferred_status_resp_msg_v01_ei;

	rc = qmi_send_req_wait(dev->qmi.handle, &req_desc, NULL, 0,
		&resp_desc, &resp, sizeof(resp), SLIM_QMI_RESP_TOUT);
	if (rc < 0) {
		SLIM_ERR(dev, "%s: QMI send req failed %d\n", __func__, rc);
		return rc;
	}
	/* Check the response */
	if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
		SLIM_ERR(dev, "%s: QMI request failed 0x%x (%s)\n",
			__func__, resp.resp.result, get_qmi_error(&resp.resp));
		return -EREMOTEIO;
	}

	/* wait for the deferred response */
	rc = wait_for_completion_timeout(&dev->qmi.defer_comp, HZ);
	if (rc == 0) {
		SLIM_WARN(dev, "slimbus power deferred response not rcvd\n");
		return -ETIMEDOUT;
	}
	/* Check what response we got in callback */
	if (dev->qmi.resp.result != QMI_RESULT_SUCCESS_V01) {
		SLIM_WARN(dev, "QMI power req failed in CB");
		return -EREMOTEIO;
	}

	return 0;
}
+5 −0
Original line number Diff line number Diff line
@@ -228,6 +228,10 @@ struct msm_slim_qmi {
	struct kthread_worker		kworker;
	struct completion		qmi_comp;
	struct notifier_block		nb;
	bool				deferred_resp;
	struct qmi_response_type_v01	resp;
	struct msg_desc			resp_desc;
	struct completion		defer_comp;
};

enum msm_slim_dom {
@@ -437,4 +441,5 @@ void msm_slim_qmi_exit(struct msm_slim_ctrl *dev);
int msm_slim_qmi_init(struct msm_slim_ctrl *dev, bool apps_is_master);
int msm_slim_qmi_power_request(struct msm_slim_ctrl *dev, bool active);
int msm_slim_qmi_check_framer_request(struct msm_slim_ctrl *dev);
int msm_slim_qmi_deferred_status_req(struct msm_slim_ctrl *dev);
#endif