aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/drivers/slimbus/qcom-ngd-ctrl.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/slimbus/qcom-ngd-ctrl.c')
-rw-r--r--drivers/slimbus/qcom-ngd-ctrl.c97
1 files changed, 93 insertions, 4 deletions
diff --git a/drivers/slimbus/qcom-ngd-ctrl.c b/drivers/slimbus/qcom-ngd-ctrl.c
index 218aefc3531c..d32e6b37514d 100644
--- a/drivers/slimbus/qcom-ngd-ctrl.c
+++ b/drivers/slimbus/qcom-ngd-ctrl.c
@@ -13,6 +13,9 @@
#include <linux/slimbus.h>
#include <linux/delay.h>
#include <linux/pm_runtime.h>
+#include <linux/mutex.h>
+#include <linux/notifier.h>
+#include <linux/remoteproc/qcom_rproc.h>
#include <linux/of.h>
#include <linux/io.h>
#include <linux/soc/qcom/qmi.h>
@@ -155,8 +158,14 @@ struct qcom_slim_ngd_ctrl {
struct qcom_slim_ngd_dma_desc txdesc[QCOM_SLIM_NGD_DESC_NUM];
struct completion reconf;
struct work_struct m_work;
+ struct work_struct ngd_up_work;
struct workqueue_struct *mwq;
+ struct completion qmi_up;
spinlock_t tx_buf_lock;
+ struct mutex tx_lock;
+ struct mutex ssr_lock;
+ struct notifier_block nb;
+ void *notifier;
enum qcom_slim_ngd_state state;
dma_addr_t rx_phys_base;
dma_addr_t tx_phys_base;
@@ -868,14 +877,18 @@ static int qcom_slim_ngd_xfer_msg(struct slim_controller *sctrl,
if (txn->msg && txn->msg->wbuf)
memcpy(puc, txn->msg->wbuf, txn->msg->num_bytes);
+ mutex_lock(&ctrl->tx_lock);
ret = qcom_slim_ngd_tx_msg_post(ctrl, pbuf, txn->rl);
- if (ret)
+ if (ret) {
+ mutex_unlock(&ctrl->tx_lock);
return ret;
+ }
timeout = wait_for_completion_timeout(&tx_sent, HZ);
if (!timeout) {
dev_err(sctrl->dev, "TX timed out:MC:0x%x,mt:0x%x", txn->mc,
txn->mt);
+ mutex_unlock(&ctrl->tx_lock);
return -ETIMEDOUT;
}
@@ -884,10 +897,12 @@ static int qcom_slim_ngd_xfer_msg(struct slim_controller *sctrl,
if (!timeout) {
dev_err(sctrl->dev, "TX timed out:MC:0x%x,mt:0x%x",
txn->mc, txn->mt);
+ mutex_unlock(&ctrl->tx_lock);
return -ETIMEDOUT;
}
}
+ mutex_unlock(&ctrl->tx_lock);
return 0;
}
@@ -1200,6 +1215,13 @@ capability_retry:
}
}
+static int qcom_slim_ngd_update_device_status(struct device *dev, void *null)
+{
+ slim_report_absent(to_slim_device(dev));
+
+ return 0;
+}
+
static int qcom_slim_ngd_runtime_resume(struct device *dev)
{
struct qcom_slim_ngd_ctrl *ctrl = dev_get_drvdata(dev);
@@ -1267,7 +1289,7 @@ static int qcom_slim_ngd_qmi_new_server(struct qmi_handle *hdl,
qmi->svc_info.sq_node = service->node;
qmi->svc_info.sq_port = service->port;
- qcom_slim_ngd_enable(ctrl, true);
+ complete(&ctrl->qmi_up);
return 0;
}
@@ -1280,10 +1302,9 @@ static void qcom_slim_ngd_qmi_del_server(struct qmi_handle *hdl,
struct qcom_slim_ngd_ctrl *ctrl =
container_of(qmi, struct qcom_slim_ngd_ctrl, qmi);
+ reinit_completion(&ctrl->qmi_up);
qmi->svc_info.sq_node = 0;
qmi->svc_info.sq_port = 0;
-
- qcom_slim_ngd_enable(ctrl, false);
}
static struct qmi_ops qcom_slim_ngd_qmi_svc_event_ops = {
@@ -1333,6 +1354,64 @@ static const struct of_device_id qcom_slim_ngd_dt_match[] = {
MODULE_DEVICE_TABLE(of, qcom_slim_ngd_dt_match);
+static void qcom_slim_ngd_down(struct qcom_slim_ngd_ctrl *ctrl)
+{
+ mutex_lock(&ctrl->ssr_lock);
+ device_for_each_child(ctrl->ctrl.dev, NULL,
+ qcom_slim_ngd_update_device_status);
+ qcom_slim_ngd_enable(ctrl, false);
+ mutex_unlock(&ctrl->ssr_lock);
+}
+
+static void qcom_slim_ngd_up_worker(struct work_struct *work)
+{
+ struct qcom_slim_ngd_ctrl *ctrl;
+
+ ctrl = container_of(work, struct qcom_slim_ngd_ctrl, ngd_up_work);
+
+ /* Make sure qmi service is up before continuing */
+ wait_for_completion_interruptible(&ctrl->qmi_up);
+
+ mutex_lock(&ctrl->ssr_lock);
+ qcom_slim_ngd_enable(ctrl, true);
+ mutex_unlock(&ctrl->ssr_lock);
+}
+
+static int qcom_slim_ngd_ssr_pdr_notify(struct qcom_slim_ngd_ctrl *ctrl,
+ unsigned long action)
+{
+ switch (action) {
+ case QCOM_SSR_BEFORE_SHUTDOWN:
+ /* Make sure the last dma xfer is finished */
+ mutex_lock(&ctrl->tx_lock);
+ if (ctrl->state != QCOM_SLIM_NGD_CTRL_DOWN) {
+ pm_runtime_get_noresume(ctrl->dev);
+ ctrl->state = QCOM_SLIM_NGD_CTRL_DOWN;
+ qcom_slim_ngd_down(ctrl);
+ qcom_slim_ngd_exit_dma(ctrl);
+ }
+ mutex_unlock(&ctrl->tx_lock);
+ break;
+ case QCOM_SSR_AFTER_POWERUP:
+ schedule_work(&ctrl->ngd_up_work);
+ break;
+ default:
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static int qcom_slim_ngd_ssr_notify(struct notifier_block *nb,
+ unsigned long action,
+ void *data)
+{
+ struct qcom_slim_ngd_ctrl *ctrl = container_of(nb,
+ struct qcom_slim_ngd_ctrl, nb);
+
+ return qcom_slim_ngd_ssr_pdr_notify(ctrl, action);
+}
+
static int of_qcom_slim_ngd_register(struct device *parent,
struct qcom_slim_ngd_ctrl *ctrl)
{
@@ -1397,6 +1476,7 @@ static int qcom_slim_ngd_probe(struct platform_device *pdev)
}
INIT_WORK(&ctrl->m_work, qcom_slim_ngd_master_worker);
+ INIT_WORK(&ctrl->ngd_up_work, qcom_slim_ngd_up_worker);
ctrl->mwq = create_singlethread_workqueue("ngd_master");
if (!ctrl->mwq) {
dev_err(&pdev->dev, "Failed to start master worker\n");
@@ -1444,6 +1524,11 @@ static int qcom_slim_ngd_ctrl_probe(struct platform_device *pdev)
return ret;
}
+ ctrl->nb.notifier_call = qcom_slim_ngd_ssr_notify;
+ ctrl->notifier = qcom_register_ssr_notifier("lpass", &ctrl->nb);
+ if (IS_ERR(ctrl->notifier))
+ return PTR_ERR(ctrl->notifier);
+
ctrl->dev = dev;
ctrl->framer.rootfreq = SLIM_ROOT_FREQ >> 3;
ctrl->framer.superfreq =
@@ -1457,9 +1542,12 @@ static int qcom_slim_ngd_ctrl_probe(struct platform_device *pdev)
ctrl->ctrl.wakeup = NULL;
ctrl->state = QCOM_SLIM_NGD_CTRL_DOWN;
+ mutex_init(&ctrl->tx_lock);
+ mutex_init(&ctrl->ssr_lock);
spin_lock_init(&ctrl->tx_buf_lock);
init_completion(&ctrl->reconf);
init_completion(&ctrl->qmi.qmi_comp);
+ init_completion(&ctrl->qmi_up);
platform_driver_register(&qcom_slim_ngd_driver);
return of_qcom_slim_ngd_register(dev, ctrl);
@@ -1477,6 +1565,7 @@ static int qcom_slim_ngd_remove(struct platform_device *pdev)
struct qcom_slim_ngd_ctrl *ctrl = platform_get_drvdata(pdev);
pm_runtime_disable(&pdev->dev);
+ qcom_unregister_ssr_notifier(ctrl->notifier, &ctrl->nb);
qcom_slim_ngd_enable(ctrl, false);
qcom_slim_ngd_exit_dma(ctrl);
qcom_slim_ngd_qmi_svc_event_deinit(&ctrl->qmi);