diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/slimbus/Kconfig | 3 | ||||
-rw-r--r-- | drivers/slimbus/qcom-ngd-ctrl.c | 97 |
2 files changed, 95 insertions, 5 deletions
diff --git a/drivers/slimbus/Kconfig b/drivers/slimbus/Kconfig index 8cd595148d17..02534ce86e46 100644 --- a/drivers/slimbus/Kconfig +++ b/drivers/slimbus/Kconfig @@ -22,9 +22,10 @@ config SLIM_QCOM_CTRL config SLIM_QCOM_NGD_CTRL tristate "Qualcomm SLIMbus Satellite Non-Generic Device Component" - depends on HAS_IOMEM && DMA_ENGINE && NET + depends on HAS_IOMEM && DMA_ENGINE && NET && REMOTEPROC depends on ARCH_QCOM || COMPILE_TEST select QCOM_QMI_HELPERS + select QCOM_RPROC_COMMON help Select driver if Qualcomm's SLIMbus Satellite Non-Generic Device Component is programmed using Linux kernel. 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); |