Skip to content

Commit aa7ec73

Browse files
krishnareddy-dvwilldeacon
authored andcommitted
iommu/arm-smmu: Add global/context fault implementation hooks
Add global/context fault hooks to allow vendor specific implementations override default fault interrupt handlers. Update NVIDIA implementation to override the default global/context fault interrupt handlers and handle interrupts across the two ARM MMU-500s that are programmed identically. Signed-off-by: Krishna Reddy <[email protected]> Reviewed-by: Jon Hunter <[email protected]> Reviewed-by: Nicolin Chen <[email protected]> Reviewed-by: Pritesh Raithatha <[email protected]> Reviewed-by: Robin Murphy <[email protected]> Reviewed-by: Thierry Reding <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Will Deacon <[email protected]>
1 parent 3d2deb0 commit aa7ec73

File tree

3 files changed

+117
-2
lines changed

3 files changed

+117
-2
lines changed

drivers/iommu/arm-smmu-nvidia.c

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,13 +127,112 @@ static int nvidia_smmu_reset(struct arm_smmu_device *smmu)
127127
return 0;
128128
}
129129

130+
static irqreturn_t nvidia_smmu_global_fault_inst(int irq,
131+
struct arm_smmu_device *smmu,
132+
int inst)
133+
{
134+
u32 gfsr, gfsynr0, gfsynr1, gfsynr2;
135+
void __iomem *gr0_base = nvidia_smmu_page(smmu, inst, 0);
136+
137+
gfsr = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSR);
138+
if (!gfsr)
139+
return IRQ_NONE;
140+
141+
gfsynr0 = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSYNR0);
142+
gfsynr1 = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSYNR1);
143+
gfsynr2 = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSYNR2);
144+
145+
dev_err_ratelimited(smmu->dev,
146+
"Unexpected global fault, this could be serious\n");
147+
dev_err_ratelimited(smmu->dev,
148+
"\tGFSR 0x%08x, GFSYNR0 0x%08x, GFSYNR1 0x%08x, GFSYNR2 0x%08x\n",
149+
gfsr, gfsynr0, gfsynr1, gfsynr2);
150+
151+
writel_relaxed(gfsr, gr0_base + ARM_SMMU_GR0_sGFSR);
152+
return IRQ_HANDLED;
153+
}
154+
155+
static irqreturn_t nvidia_smmu_global_fault(int irq, void *dev)
156+
{
157+
unsigned int inst;
158+
irqreturn_t ret = IRQ_NONE;
159+
struct arm_smmu_device *smmu = dev;
160+
161+
for (inst = 0; inst < NUM_SMMU_INSTANCES; inst++) {
162+
irqreturn_t irq_ret;
163+
164+
irq_ret = nvidia_smmu_global_fault_inst(irq, smmu, inst);
165+
if (irq_ret == IRQ_HANDLED)
166+
ret = IRQ_HANDLED;
167+
}
168+
169+
return ret;
170+
}
171+
172+
static irqreturn_t nvidia_smmu_context_fault_bank(int irq,
173+
struct arm_smmu_device *smmu,
174+
int idx, int inst)
175+
{
176+
u32 fsr, fsynr, cbfrsynra;
177+
unsigned long iova;
178+
void __iomem *gr1_base = nvidia_smmu_page(smmu, inst, 1);
179+
void __iomem *cb_base = nvidia_smmu_page(smmu, inst, smmu->numpage + idx);
180+
181+
fsr = readl_relaxed(cb_base + ARM_SMMU_CB_FSR);
182+
if (!(fsr & ARM_SMMU_FSR_FAULT))
183+
return IRQ_NONE;
184+
185+
fsynr = readl_relaxed(cb_base + ARM_SMMU_CB_FSYNR0);
186+
iova = readq_relaxed(cb_base + ARM_SMMU_CB_FAR);
187+
cbfrsynra = readl_relaxed(gr1_base + ARM_SMMU_GR1_CBFRSYNRA(idx));
188+
189+
dev_err_ratelimited(smmu->dev,
190+
"Unhandled context fault: fsr=0x%x, iova=0x%08lx, fsynr=0x%x, cbfrsynra=0x%x, cb=%d\n",
191+
fsr, iova, fsynr, cbfrsynra, idx);
192+
193+
writel_relaxed(fsr, cb_base + ARM_SMMU_CB_FSR);
194+
return IRQ_HANDLED;
195+
}
196+
197+
static irqreturn_t nvidia_smmu_context_fault(int irq, void *dev)
198+
{
199+
int idx;
200+
unsigned int inst;
201+
irqreturn_t ret = IRQ_NONE;
202+
struct arm_smmu_device *smmu;
203+
struct iommu_domain *domain = dev;
204+
struct arm_smmu_domain *smmu_domain;
205+
206+
smmu_domain = container_of(domain, struct arm_smmu_domain, domain);
207+
smmu = smmu_domain->smmu;
208+
209+
for (inst = 0; inst < NUM_SMMU_INSTANCES; inst++) {
210+
irqreturn_t irq_ret;
211+
212+
/*
213+
* Interrupt line is shared between all contexts.
214+
* Check for faults across all contexts.
215+
*/
216+
for (idx = 0; idx < smmu->num_context_banks; idx++) {
217+
irq_ret = nvidia_smmu_context_fault_bank(irq, smmu,
218+
idx, inst);
219+
if (irq_ret == IRQ_HANDLED)
220+
ret = IRQ_HANDLED;
221+
}
222+
}
223+
224+
return ret;
225+
}
226+
130227
static const struct arm_smmu_impl nvidia_smmu_impl = {
131228
.read_reg = nvidia_smmu_read_reg,
132229
.write_reg = nvidia_smmu_write_reg,
133230
.read_reg64 = nvidia_smmu_read_reg64,
134231
.write_reg64 = nvidia_smmu_write_reg64,
135232
.reset = nvidia_smmu_reset,
136233
.tlb_sync = nvidia_smmu_tlb_sync,
234+
.global_fault = nvidia_smmu_global_fault,
235+
.context_fault = nvidia_smmu_context_fault,
137236
};
138237

139238
struct arm_smmu_device *nvidia_smmu_impl_init(struct arm_smmu_device *smmu)

drivers/iommu/arm-smmu.c

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -670,6 +670,7 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
670670
enum io_pgtable_fmt fmt;
671671
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
672672
struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
673+
irqreturn_t (*context_fault)(int irq, void *dev);
673674

674675
mutex_lock(&smmu_domain->init_mutex);
675676
if (smmu_domain->smmu)
@@ -832,7 +833,13 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
832833
* handler seeing a half-initialised domain state.
833834
*/
834835
irq = smmu->irqs[smmu->num_global_irqs + cfg->irptndx];
835-
ret = devm_request_irq(smmu->dev, irq, arm_smmu_context_fault,
836+
837+
if (smmu->impl && smmu->impl->context_fault)
838+
context_fault = smmu->impl->context_fault;
839+
else
840+
context_fault = arm_smmu_context_fault;
841+
842+
ret = devm_request_irq(smmu->dev, irq, context_fault,
836843
IRQF_SHARED, "arm-smmu-context-fault", domain);
837844
if (ret < 0) {
838845
dev_err(smmu->dev, "failed to request context IRQ %d (%u)\n",
@@ -2108,6 +2115,7 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
21082115
struct arm_smmu_device *smmu;
21092116
struct device *dev = &pdev->dev;
21102117
int num_irqs, i, err;
2118+
irqreturn_t (*global_fault)(int irq, void *dev);
21112119

21122120
smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL);
21132121
if (!smmu) {
@@ -2194,9 +2202,14 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
21942202
smmu->num_context_irqs = smmu->num_context_banks;
21952203
}
21962204

2205+
if (smmu->impl && smmu->impl->global_fault)
2206+
global_fault = smmu->impl->global_fault;
2207+
else
2208+
global_fault = arm_smmu_global_fault;
2209+
21972210
for (i = 0; i < smmu->num_global_irqs; ++i) {
21982211
err = devm_request_irq(smmu->dev, smmu->irqs[i],
2199-
arm_smmu_global_fault,
2212+
global_fault,
22002213
IRQF_SHARED,
22012214
"arm-smmu global fault",
22022215
smmu);

drivers/iommu/arm-smmu.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <linux/io-64-nonatomic-hi-lo.h>
1919
#include <linux/io-pgtable.h>
2020
#include <linux/iommu.h>
21+
#include <linux/irqreturn.h>
2122
#include <linux/mutex.h>
2223
#include <linux/spinlock.h>
2324
#include <linux/types.h>
@@ -389,6 +390,8 @@ struct arm_smmu_impl {
389390
void (*tlb_sync)(struct arm_smmu_device *smmu, int page, int sync,
390391
int status);
391392
int (*def_domain_type)(struct device *dev);
393+
irqreturn_t (*global_fault)(int irq, void *dev);
394+
irqreturn_t (*context_fault)(int irq, void *dev);
392395
};
393396

394397
static inline void __iomem *arm_smmu_page(struct arm_smmu_device *smmu, int n)

0 commit comments

Comments
 (0)