[PATCH 3/3] iommu/arm-smmu-v3: support suspend/resume

From: Peng Fan (OSS)
Date: Sun Mar 24 2024 - 08:21:37 EST


From: Peng Fan <peng.fan@xxxxxxx>

smmu maybe power-gated, and the registers are cleared. So
need to restore its registers with arm_smmu_device_reset and
resume the msi interrupt settings in resume callback.

Signed-off-by: Peng Fan <peng.fan@xxxxxxx>
---
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 71 ++++++++++++++++++++++++++---
1 file changed, 64 insertions(+), 7 deletions(-)

diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
index a8a569573c2f..2bfe4b3d0ba1 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -3396,6 +3396,36 @@ static void arm_smmu_setup_msis(struct arm_smmu_device *smmu)
devm_add_action(dev, arm_smmu_free_msis, dev);
}

+static void arm_smmu_resume_unique_irqs(struct arm_smmu_device *smmu)
+{
+ struct device *dev = smmu->dev;
+ struct msi_desc *desc;
+ struct msi_msg msg;
+
+ if (!dev->msi.domain)
+ return;
+
+ desc = irq_get_msi_desc(smmu->evtq.q.irq);
+ if (desc) {
+ get_cached_msi_msg(smmu->evtq.q.irq, &msg);
+ arm_smmu_write_msi_msg(desc, &msg);
+ }
+
+ desc = irq_get_msi_desc(smmu->gerr_irq);
+ if (desc) {
+ get_cached_msi_msg(smmu->gerr_irq, &msg);
+ arm_smmu_write_msi_msg(desc, &msg);
+ }
+
+ if (smmu->features & ARM_SMMU_FEAT_PRI) {
+ desc = irq_get_msi_desc(smmu->priq.q.irq);
+ if (desc) {
+ get_cached_msi_msg(smmu->priq.q.irq, &msg);
+ arm_smmu_write_msi_msg(desc, &msg);
+ }
+ }
+}
+
static void arm_smmu_setup_unique_irqs(struct arm_smmu_device *smmu)
{
int irq, ret;
@@ -3442,7 +3472,7 @@ static void arm_smmu_setup_unique_irqs(struct arm_smmu_device *smmu)
}
}

-static int arm_smmu_setup_irqs(struct arm_smmu_device *smmu)
+static int arm_smmu_setup_irqs(struct arm_smmu_device *smmu, bool resume)
{
int ret, irq;
u32 irqen_flags = IRQ_CTRL_EVTQ_IRQEN | IRQ_CTRL_GERROR_IRQEN;
@@ -3456,7 +3486,7 @@ static int arm_smmu_setup_irqs(struct arm_smmu_device *smmu)
}

irq = smmu->combined_irq;
- if (irq) {
+ if (irq && !resume) {
/*
* Cavium ThunderX2 implementation doesn't support unique irq
* lines. Use a single irq line for all the SMMUv3 interrupts.
@@ -3468,8 +3498,12 @@ static int arm_smmu_setup_irqs(struct arm_smmu_device *smmu)
"arm-smmu-v3-combined-irq", smmu);
if (ret < 0)
dev_warn(smmu->dev, "failed to enable combined irq\n");
- } else
- arm_smmu_setup_unique_irqs(smmu);
+ } else {
+ if (resume)
+ arm_smmu_resume_unique_irqs(smmu);
+ else
+ arm_smmu_setup_unique_irqs(smmu);
+ }

if (smmu->features & ARM_SMMU_FEAT_PRI)
irqen_flags |= IRQ_CTRL_PRIQ_IRQEN;
@@ -3494,7 +3528,7 @@ static int arm_smmu_device_disable(struct arm_smmu_device *smmu)
return ret;
}

-static int arm_smmu_device_reset(struct arm_smmu_device *smmu)
+static int arm_smmu_device_reset(struct arm_smmu_device *smmu, bool resume)
{
int ret;
u32 reg, enables;
@@ -3602,7 +3636,7 @@ static int arm_smmu_device_reset(struct arm_smmu_device *smmu)
}
}

- ret = arm_smmu_setup_irqs(smmu);
+ ret = arm_smmu_setup_irqs(smmu, resume);
if (ret) {
dev_err(smmu->dev, "failed to setup irqs\n");
return ret;
@@ -4086,7 +4120,7 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
arm_smmu_rmr_install_bypass_ste(smmu);

/* Reset the device */
- ret = arm_smmu_device_reset(smmu);
+ ret = arm_smmu_device_reset(smmu, false);
if (ret)
return ret;

@@ -4124,12 +4158,34 @@ static void arm_smmu_device_shutdown(struct platform_device *pdev)
arm_smmu_device_disable(smmu);
}

+static int __maybe_unused arm_smmu_suspend(struct device *dev)
+{
+ struct arm_smmu_device *smmu = dev_get_drvdata(dev);
+
+ arm_smmu_device_disable(smmu);
+
+ return 0;
+}
+
+static int __maybe_unused arm_smmu_resume(struct device *dev)
+{
+ struct arm_smmu_device *smmu = dev_get_drvdata(dev);
+
+ arm_smmu_device_reset(smmu, true);
+
+ return 0;
+}
+
static const struct of_device_id arm_smmu_of_match[] = {
{ .compatible = "arm,smmu-v3", },
{ },
};
MODULE_DEVICE_TABLE(of, arm_smmu_of_match);

+static const struct dev_pm_ops arm_smmu_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(arm_smmu_suspend, arm_smmu_resume)
+};
+
static void arm_smmu_driver_unregister(struct platform_driver *drv)
{
arm_smmu_sva_notifier_synchronize();
@@ -4141,6 +4197,7 @@ static struct platform_driver arm_smmu_driver = {
.name = "arm-smmu-v3",
.of_match_table = arm_smmu_of_match,
.suppress_bind_attrs = true,
+ .pm = &arm_smmu_pm_ops,
},
.probe = arm_smmu_device_probe,
.remove_new = arm_smmu_device_remove,

--
2.37.1