mirror of
https://github.com/torvalds/linux.git
synced 2025-12-07 20:06:24 +00:00
accel/qaic: Add support for PM callbacks
Add initial support for suspend and hibernation PM callbacks to QAIC. The device can be suspended any time in which the data path is not busy as queued I/O operations are lost on suspension and cannot be resumed after suspend. Signed-off-by: Youssef Samir <youssef.abdulrahman@oss.qualcomm.com> Reviewed-by: Carl Vanderlip <carl.vanderlip@oss.qualcomm.com> Signed-off-by: Zack McKevitt <zachary.mckevitt@oss.qualcomm.com> Reviewed-by: Jeff Hugo <jeff.hugo@oss.qualcomm.com> Signed-off-by: Jeff Hugo <jeff.hugo@oss.qualcomm.com> Link: https://patch.msgid.link/20251029181808.1216466-1-zachary.mckevitt@oss.qualcomm.com
This commit is contained in:
@@ -161,6 +161,8 @@ struct qaic_device {
|
||||
struct mhi_device *qts_ch;
|
||||
/* Work queue for tasks related to MHI "QAIC_TIMESYNC" channel */
|
||||
struct workqueue_struct *qts_wq;
|
||||
/* MHI "QAIC_TIMESYNC_PERIODIC" channel device */
|
||||
struct mhi_device *mqts_ch;
|
||||
/* Head of list of page allocated by MHI bootlog device */
|
||||
struct list_head bootlog;
|
||||
/* MHI bootlog channel device */
|
||||
|
||||
@@ -660,6 +660,92 @@ static const struct pci_error_handlers qaic_pci_err_handler = {
|
||||
.reset_done = qaic_pci_reset_done,
|
||||
};
|
||||
|
||||
static bool qaic_is_under_reset(struct qaic_device *qdev)
|
||||
{
|
||||
int rcu_id;
|
||||
bool ret;
|
||||
|
||||
rcu_id = srcu_read_lock(&qdev->dev_lock);
|
||||
ret = qdev->dev_state != QAIC_ONLINE;
|
||||
srcu_read_unlock(&qdev->dev_lock, rcu_id);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool qaic_data_path_busy(struct qaic_device *qdev)
|
||||
{
|
||||
bool ret = false;
|
||||
int dev_rcu_id;
|
||||
int i;
|
||||
|
||||
dev_rcu_id = srcu_read_lock(&qdev->dev_lock);
|
||||
if (qdev->dev_state != QAIC_ONLINE) {
|
||||
srcu_read_unlock(&qdev->dev_lock, dev_rcu_id);
|
||||
return false;
|
||||
}
|
||||
for (i = 0; i < qdev->num_dbc; i++) {
|
||||
struct dma_bridge_chan *dbc = &qdev->dbc[i];
|
||||
unsigned long flags;
|
||||
int ch_rcu_id;
|
||||
|
||||
ch_rcu_id = srcu_read_lock(&dbc->ch_lock);
|
||||
if (!dbc->usr || !dbc->in_use) {
|
||||
srcu_read_unlock(&dbc->ch_lock, ch_rcu_id);
|
||||
continue;
|
||||
}
|
||||
spin_lock_irqsave(&dbc->xfer_lock, flags);
|
||||
ret = !list_empty(&dbc->xfer_list);
|
||||
spin_unlock_irqrestore(&dbc->xfer_lock, flags);
|
||||
srcu_read_unlock(&dbc->ch_lock, ch_rcu_id);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
srcu_read_unlock(&qdev->dev_lock, dev_rcu_id);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qaic_pm_suspend(struct device *dev)
|
||||
{
|
||||
struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(dev));
|
||||
|
||||
dev_dbg(dev, "Suspending..\n");
|
||||
if (qaic_data_path_busy(qdev)) {
|
||||
dev_dbg(dev, "Device's datapath is busy. Aborting suspend..\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
if (qaic_is_under_reset(qdev)) {
|
||||
dev_dbg(dev, "Device is under reset. Aborting suspend..\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
qaic_mqts_ch_stop_timer(qdev->mqts_ch);
|
||||
qaic_pci_reset_prepare(qdev->pdev);
|
||||
pci_save_state(qdev->pdev);
|
||||
pci_disable_device(qdev->pdev);
|
||||
pci_set_power_state(qdev->pdev, PCI_D3hot);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qaic_pm_resume(struct device *dev)
|
||||
{
|
||||
struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(dev));
|
||||
int ret;
|
||||
|
||||
dev_dbg(dev, "Resuming..\n");
|
||||
pci_set_power_state(qdev->pdev, PCI_D0);
|
||||
pci_restore_state(qdev->pdev);
|
||||
ret = pci_enable_device(qdev->pdev);
|
||||
if (ret) {
|
||||
dev_err(dev, "pci_enable_device failed on resume %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
pci_set_master(qdev->pdev);
|
||||
qaic_pci_reset_done(qdev->pdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops qaic_pm_ops = {
|
||||
SYSTEM_SLEEP_PM_OPS(qaic_pm_suspend, qaic_pm_resume)
|
||||
};
|
||||
|
||||
static struct pci_driver qaic_pci_driver = {
|
||||
.name = QAIC_NAME,
|
||||
.id_table = qaic_ids,
|
||||
@@ -667,6 +753,9 @@ static struct pci_driver qaic_pci_driver = {
|
||||
.remove = qaic_pci_remove,
|
||||
.shutdown = qaic_pci_shutdown,
|
||||
.err_handler = &qaic_pci_err_handler,
|
||||
.driver = {
|
||||
.pm = pm_sleep_ptr(&qaic_pm_ops),
|
||||
},
|
||||
};
|
||||
|
||||
static int __init qaic_init(void)
|
||||
|
||||
@@ -171,6 +171,13 @@ mod_timer:
|
||||
dev_err(mqtsdev->dev, "%s mod_timer error:%d\n", __func__, ret);
|
||||
}
|
||||
|
||||
void qaic_mqts_ch_stop_timer(struct mhi_device *mhi_dev)
|
||||
{
|
||||
struct mqts_dev *mqtsdev = dev_get_drvdata(&mhi_dev->dev);
|
||||
|
||||
timer_delete_sync(&mqtsdev->timer);
|
||||
}
|
||||
|
||||
static int qaic_timesync_probe(struct mhi_device *mhi_dev, const struct mhi_device_id *id)
|
||||
{
|
||||
struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(mhi_dev->mhi_cntrl->cntrl_dev));
|
||||
@@ -206,6 +213,7 @@ static int qaic_timesync_probe(struct mhi_device *mhi_dev, const struct mhi_devi
|
||||
timer->expires = jiffies + msecs_to_jiffies(timesync_delay_ms);
|
||||
add_timer(timer);
|
||||
dev_set_drvdata(&mhi_dev->dev, mqtsdev);
|
||||
qdev->mqts_ch = mhi_dev;
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -221,6 +229,7 @@ static void qaic_timesync_remove(struct mhi_device *mhi_dev)
|
||||
{
|
||||
struct mqts_dev *mqtsdev = dev_get_drvdata(&mhi_dev->dev);
|
||||
|
||||
mqtsdev->qdev->mqts_ch = NULL;
|
||||
timer_delete_sync(&mqtsdev->timer);
|
||||
mhi_unprepare_from_transfer(mqtsdev->mhi_dev);
|
||||
kfree(mqtsdev->sync_msg);
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
#ifndef __QAIC_TIMESYNC_H__
|
||||
#define __QAIC_TIMESYNC_H__
|
||||
|
||||
#include <linux/mhi.h>
|
||||
|
||||
int qaic_timesync_init(void);
|
||||
void qaic_timesync_deinit(void);
|
||||
void qaic_mqts_ch_stop_timer(struct mhi_device *mhi_dev);
|
||||
#endif /* __QAIC_TIMESYNC_H__ */
|
||||
|
||||
Reference in New Issue
Block a user