forked from neggles/copr-linux-phytium
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path0093-iommu-arm-smmu-v3-Add-suspend-and-resume-support.patch
236 lines (212 loc) · 7.44 KB
/
0093-iommu-arm-smmu-v3-Add-suspend-and-resume-support.patch
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
From 44e13387d11e4e0b525067294d36e6e3775137a4 Mon Sep 17 00:00:00 2001
From: Bixuan Cui <[email protected]>
Date: Mon, 17 Jun 2024 19:33:08 +0800
Subject: [PATCH 093/150] iommu/arm-smmu-v3: Add suspend and resume support
Add suspend and resume support for smmuv3. The smmu is stopped when
suspending and started when resuming.
When the smmu is suspended, it is powered off and the registers are
cleared. So saves the msi_msg context during msi interrupt initialization
of smmu. When resume happens it calls arm_smmu_device_reset() to restore
the registers.
Closes: https://gitee.com/openeuler/kernel/issues/I4DZ7Q
Signed-off-by: Bixuan Cui <[email protected]>
Signed-off-by: Zhou Guanghui <[email protected]>
Reviewed-by: Hanjun Guo <[email protected]>
Signed-off-by: Yang Yingliang <[email protected]>
Signed-off-by: Wang Xu <[email protected]>
Signed-off-by: Li Mingzhe <[email protected]>
Signed-off-by: Chen Baozi <[email protected]>
Change-Id: I8e86de9ced49fff1206519739f730cc20d2c4d66
Signed-off-by: Andrew Powers-Holmes <[email protected]>
---
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 97 ++++++++++++++++++---
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 1 +
2 files changed, 88 insertions(+), 10 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 68b81f9c2f4b..8ef0e1a766b1 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -3154,6 +3154,13 @@ static void arm_smmu_write_msi_msg(struct msi_desc *desc, struct msi_msg *msg)
doorbell = (((u64)msg->address_hi) << 32) | msg->address_lo;
doorbell &= MSI_CFG0_ADDR_MASK;
+#ifdef CONFIG_PM_SLEEP
+ /* Saves the msg (base addr of msi irq) and restores it during resume */
+ desc->msg.address_lo = msg->address_lo;
+ desc->msg.address_hi = msg->address_hi;
+ desc->msg.data = msg->data;
+#endif
+
writeq_relaxed(doorbell, smmu->base + cfg[0]);
writel_relaxed(msg->data, smmu->base + cfg[1]);
writel_relaxed(ARM_SMMU_MEMATTR_DEVICE_nGnRE, smmu->base + cfg[2]);
@@ -3196,11 +3203,53 @@ static void arm_smmu_setup_msis(struct arm_smmu_device *smmu)
devm_add_action_or_reset(dev, arm_smmu_free_msis, dev);
}
-static void arm_smmu_setup_unique_irqs(struct arm_smmu_device *smmu)
+#ifdef CONFIG_PM_SLEEP
+static void arm_smmu_resume_msis(struct arm_smmu_device *smmu)
+{
+ struct msi_desc *desc;
+ struct device *dev = smmu->dev;
+
+ if (!dev->msi.domain)
+ return;
+
+ msi_for_each_desc(desc, dev, MSI_DESC_ASSOCIATED) {
+ switch (desc->msi_index) {
+ case EVTQ_MSI_INDEX:
+ case GERROR_MSI_INDEX:
+ case PRIQ_MSI_INDEX: {
+ phys_addr_t *cfg = arm_smmu_msi_cfg[desc->msi_index];
+ struct msi_msg *msg = &desc->msg;
+ phys_addr_t doorbell = (((u64)msg->address_hi) << 32) | msg->address_lo;
+
+ doorbell &= MSI_CFG0_ADDR_MASK;
+ writeq_relaxed(doorbell, smmu->base + cfg[0]);
+ writel_relaxed(msg->data, smmu->base + cfg[1]);
+ writel_relaxed(ARM_SMMU_MEMATTR_DEVICE_nGnRE,
+ smmu->base + cfg[2]);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+}
+#else
+static void arm_smmu_resume_msis(struct arm_smmu_device *smmu)
+{
+}
+#endif
+
+static void arm_smmu_setup_unique_irqs(struct arm_smmu_device *smmu, bool resume)
{
int irq, ret;
- arm_smmu_setup_msis(smmu);
+ if (!resume)
+ arm_smmu_setup_msis(smmu);
+ else {
+ /* The irq doesn't need to be re-requested during resume */
+ arm_smmu_resume_msis(smmu);
+ return;
+ }
/* Request interrupt lines */
irq = smmu->evtq.q.irq;
@@ -3242,7 +3291,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;
@@ -3269,7 +3318,7 @@ static int arm_smmu_setup_irqs(struct arm_smmu_device *smmu)
if (ret < 0)
dev_warn(smmu->dev, "failed to enable combined irq\n");
} else
- arm_smmu_setup_unique_irqs(smmu);
+ arm_smmu_setup_unique_irqs(smmu, resume);
if (smmu->features & ARM_SMMU_FEAT_PRI)
irqen_flags |= IRQ_CTRL_PRIQ_IRQEN;
@@ -3294,7 +3343,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, bool bypass)
+static int arm_smmu_device_reset(struct arm_smmu_device *smmu, bool resume)
{
int ret;
u32 reg, enables;
@@ -3402,7 +3451,7 @@ static int arm_smmu_device_reset(struct arm_smmu_device *smmu, bool bypass)
}
}
- 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;
@@ -3412,7 +3461,7 @@ static int arm_smmu_device_reset(struct arm_smmu_device *smmu, bool bypass)
enables &= ~(CR0_EVTQEN | CR0_PRIQEN);
/* Enable the SMMU interface, or ensure bypass */
- if (!bypass || disable_bypass) {
+ if (!smmu->bypass || disable_bypass) {
enables |= CR0_SMMUEN;
} else {
ret = arm_smmu_update_gbpa(smmu, 0, GBPA_ABORT);
@@ -3797,6 +3846,24 @@ static void arm_smmu_rmr_install_bypass_ste(struct arm_smmu_device *smmu)
iort_put_rmr_sids(dev_fwnode(smmu->dev), &rmr_list);
}
+#ifdef CONFIG_PM_SLEEP
+static int arm_smmu_suspend(struct device *dev)
+{
+ /*
+ * The smmu is powered off and related registers are automatically
+ * cleared when suspend. No need to do anything.
+ */
+ return 0;
+}
+static int arm_smmu_resume(struct device *dev)
+{
+ struct arm_smmu_device *smmu = dev_get_drvdata(dev);
+
+ arm_smmu_device_reset(smmu, true);
+ return 0;
+}
+#endif
+
static int arm_smmu_device_probe(struct platform_device *pdev)
{
int irq, ret;
@@ -3804,7 +3871,6 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
resource_size_t ioaddr;
struct arm_smmu_device *smmu;
struct device *dev = &pdev->dev;
- bool bypass;
smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL);
if (!smmu)
@@ -3820,7 +3886,7 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
}
/* Set bypass mode according to firmware probing result */
- bypass = !!ret;
+ smmu->bypass = !!ret;
/* Base address */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -3884,7 +3950,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, bypass);
+ ret = arm_smmu_device_reset(smmu, false);
if (ret)
return ret;
@@ -3934,10 +4000,21 @@ static void arm_smmu_driver_unregister(struct platform_driver *drv)
platform_driver_unregister(drv);
}
+#ifdef CONFIG_PM_SLEEP
+static const struct dev_pm_ops arm_smmu_pm_ops = {
+ .suspend = arm_smmu_suspend,
+ .resume = arm_smmu_resume,
+};
+#define ARM_SMMU_PM_OPS (&arm_smmu_pm_ops)
+#else
+#define ARM_SMMU_PM_OPS NULL
+#endif
+
static struct platform_driver arm_smmu_driver = {
.driver = {
.name = "arm-smmu-v3",
.of_match_table = arm_smmu_of_match,
+ .pm = ARM_SMMU_PM_OPS,
.suppress_bind_attrs = true,
},
.probe = arm_smmu_device_probe,
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
index 9915850dd4db..9cb3864b1114 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
@@ -682,6 +682,7 @@ struct arm_smmu_device {
struct rb_root streams;
struct mutex streams_mutex;
+ bool bypass;
};
struct arm_smmu_stream {
--
2.47.0