Commit 3301ef0a authored by Youssef Samir's avatar Youssef Samir Committed by Jeff Hugo
Browse files

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: default avatarYoussef Samir <youssef.abdulrahman@oss.qualcomm.com>
Reviewed-by: default avatarCarl Vanderlip <carl.vanderlip@oss.qualcomm.com>
Signed-off-by: default avatarZack McKevitt <zachary.mckevitt@oss.qualcomm.com>
Reviewed-by: default avatarJeff Hugo <jeff.hugo@oss.qualcomm.com>
Signed-off-by: default avatarJeff Hugo <jeff.hugo@oss.qualcomm.com>
Link: https://patch.msgid.link/20251029181808.1216466-1-zachary.mckevitt@oss.qualcomm.com
parent 3a0ff7b9
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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 */
+89 −0
Original line number Diff line number Diff line
@@ -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)
+9 −0
Original line number Diff line number Diff line
@@ -171,6 +171,13 @@ static void qaic_timesync_timer(struct timer_list *t)
		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);
+3 −0
Original line number Diff line number Diff line
@@ -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__ */