@@ -185,38 +185,36 @@ static sysmmu_pte_t *page_entry(sysmmu_pte_t *sent, sysmmu_iova_t iova)
185
185
lv2table_base (sent )) + lv2ent_offset (iova );
186
186
}
187
187
188
- /*
189
- * IOMMU fault information register
190
- */
191
- struct sysmmu_fault_info {
192
- unsigned int bit ; /* bit number in STATUS register */
193
- unsigned short addr_reg ; /* register to read VA fault address */
188
+ struct sysmmu_fault {
189
+ sysmmu_iova_t addr ; /* IOVA address that caused fault */
190
+ const char * name ; /* human readable fault name */
191
+ unsigned int type ; /* fault type for report_iommu_fault() */
192
+ };
193
+
194
+ struct sysmmu_v1_fault_info {
195
+ unsigned short addr_reg ; /* register to read IOVA fault address */
194
196
const char * name ; /* human readable fault name */
195
197
unsigned int type ; /* fault type for report_iommu_fault */
196
198
};
197
199
198
- static const struct sysmmu_fault_info sysmmu_faults [] = {
199
- { 0 , REG_PAGE_FAULT_ADDR , "PAGE" , IOMMU_FAULT_READ },
200
- { 1 , REG_AR_FAULT_ADDR , "AR MULTI-HIT" , IOMMU_FAULT_READ },
201
- { 2 , REG_AW_FAULT_ADDR , "AW MULTI-HIT" , IOMMU_FAULT_WRITE },
202
- { 3 , REG_DEFAULT_SLAVE_ADDR , "BUS ERROR" , IOMMU_FAULT_READ },
203
- { 4 , REG_AR_FAULT_ADDR , "AR SECURITY PROTECTION" , IOMMU_FAULT_READ },
204
- { 5 , REG_AR_FAULT_ADDR , "AR ACCESS PROTECTION" , IOMMU_FAULT_READ },
205
- { 6 , REG_AW_FAULT_ADDR , "AW SECURITY PROTECTION" , IOMMU_FAULT_WRITE },
206
- { 7 , REG_AW_FAULT_ADDR , "AW ACCESS PROTECTION" , IOMMU_FAULT_WRITE },
200
+ static const struct sysmmu_v1_fault_info sysmmu_v1_faults [] = {
201
+ { REG_PAGE_FAULT_ADDR , "PAGE" , IOMMU_FAULT_READ },
202
+ { REG_AR_FAULT_ADDR , "MULTI-HIT" , IOMMU_FAULT_READ },
203
+ { REG_AW_FAULT_ADDR , "MULTI-HIT" , IOMMU_FAULT_WRITE },
204
+ { REG_DEFAULT_SLAVE_ADDR , "BUS ERROR" , IOMMU_FAULT_READ },
205
+ { REG_AR_FAULT_ADDR , "SECURITY PROTECTION" , IOMMU_FAULT_READ },
206
+ { REG_AR_FAULT_ADDR , "ACCESS PROTECTION" , IOMMU_FAULT_READ },
207
+ { REG_AW_FAULT_ADDR , "SECURITY PROTECTION" , IOMMU_FAULT_WRITE },
208
+ { REG_AW_FAULT_ADDR , "ACCESS PROTECTION" , IOMMU_FAULT_WRITE },
207
209
};
208
210
209
- static const struct sysmmu_fault_info sysmmu_v5_faults [] = {
210
- { 0 , REG_V5_FAULT_AR_VA , "AR PTW" , IOMMU_FAULT_READ },
211
- { 1 , REG_V5_FAULT_AR_VA , "AR PAGE" , IOMMU_FAULT_READ },
212
- { 2 , REG_V5_FAULT_AR_VA , "AR MULTI-HIT" , IOMMU_FAULT_READ },
213
- { 3 , REG_V5_FAULT_AR_VA , "AR ACCESS PROTECTION" , IOMMU_FAULT_READ },
214
- { 4 , REG_V5_FAULT_AR_VA , "AR SECURITY PROTECTION" , IOMMU_FAULT_READ },
215
- { 16 , REG_V5_FAULT_AW_VA , "AW PTW" , IOMMU_FAULT_WRITE },
216
- { 17 , REG_V5_FAULT_AW_VA , "AW PAGE" , IOMMU_FAULT_WRITE },
217
- { 18 , REG_V5_FAULT_AW_VA , "AW MULTI-HIT" , IOMMU_FAULT_WRITE },
218
- { 19 , REG_V5_FAULT_AW_VA , "AW ACCESS PROTECTION" , IOMMU_FAULT_WRITE },
219
- { 20 , REG_V5_FAULT_AW_VA , "AW SECURITY PROTECTION" , IOMMU_FAULT_WRITE },
211
+ /* SysMMU v5 has the same faults for AR (0..4 bits) and AW (16..20 bits) */
212
+ static const char * const sysmmu_v5_fault_names [] = {
213
+ "PTW" ,
214
+ "PAGE" ,
215
+ "MULTI-HIT" ,
216
+ "ACCESS PROTECTION" ,
217
+ "SECURITY PROTECTION"
220
218
};
221
219
222
220
/*
@@ -246,9 +244,12 @@ struct exynos_iommu_domain {
246
244
struct iommu_domain domain ; /* generic domain data structure */
247
245
};
248
246
247
+ struct sysmmu_drvdata ;
248
+
249
249
/*
250
250
* SysMMU version specific data. Contains offsets for the registers which can
251
251
* be found in different SysMMU variants, but have different offset values.
252
+ * Also contains version specific callbacks to abstract the hardware.
252
253
*/
253
254
struct sysmmu_variant {
254
255
u32 pt_base ; /* page table base address (physical) */
@@ -259,6 +260,9 @@ struct sysmmu_variant {
259
260
u32 flush_end ; /* end address of range invalidation */
260
261
u32 int_status ; /* interrupt status information */
261
262
u32 int_clear ; /* clear the interrupt */
263
+
264
+ int (* get_fault_info )(struct sysmmu_drvdata * data , unsigned int itype ,
265
+ struct sysmmu_fault * fault );
262
266
};
263
267
264
268
/*
@@ -293,13 +297,55 @@ struct sysmmu_drvdata {
293
297
294
298
#define SYSMMU_REG (data , reg ) ((data)->sfrbase + (data)->variant->reg)
295
299
300
+ static int exynos_sysmmu_v1_get_fault_info (struct sysmmu_drvdata * data ,
301
+ unsigned int itype ,
302
+ struct sysmmu_fault * fault )
303
+ {
304
+ const struct sysmmu_v1_fault_info * finfo ;
305
+
306
+ if (itype >= ARRAY_SIZE (sysmmu_v1_faults ))
307
+ return - ENXIO ;
308
+
309
+ finfo = & sysmmu_v1_faults [itype ];
310
+ fault -> addr = readl (data -> sfrbase + finfo -> addr_reg );
311
+ fault -> name = finfo -> name ;
312
+ fault -> type = finfo -> type ;
313
+
314
+ return 0 ;
315
+ }
316
+
317
+ static int exynos_sysmmu_v5_get_fault_info (struct sysmmu_drvdata * data ,
318
+ unsigned int itype ,
319
+ struct sysmmu_fault * fault )
320
+ {
321
+ unsigned int addr_reg ;
322
+
323
+ if (itype < ARRAY_SIZE (sysmmu_v5_fault_names )) {
324
+ fault -> type = IOMMU_FAULT_READ ;
325
+ addr_reg = REG_V5_FAULT_AR_VA ;
326
+ } else if (itype >= 16 && itype <= 20 ) {
327
+ fault -> type = IOMMU_FAULT_WRITE ;
328
+ addr_reg = REG_V5_FAULT_AW_VA ;
329
+ itype -= 16 ;
330
+ } else {
331
+ return - ENXIO ;
332
+ }
333
+
334
+ fault -> name = sysmmu_v5_fault_names [itype ];
335
+ fault -> addr = readl (data -> sfrbase + addr_reg );
336
+
337
+ return 0 ;
338
+ }
339
+
296
340
/* SysMMU v1..v3 */
297
341
static const struct sysmmu_variant sysmmu_v1_variant = {
298
342
.flush_all = 0x0c ,
299
343
.flush_entry = 0x10 ,
300
344
.pt_base = 0x14 ,
301
345
.int_status = 0x18 ,
302
346
.int_clear = 0x1c ,
347
+
348
+ .get_fault_info = exynos_sysmmu_v1_get_fault_info ,
303
349
};
304
350
305
351
/* SysMMU v5 and v7 (non-VM capable) */
@@ -312,6 +358,8 @@ static const struct sysmmu_variant sysmmu_v5_variant = {
312
358
.flush_end = 0x24 ,
313
359
.int_status = 0x60 ,
314
360
.int_clear = 0x64 ,
361
+
362
+ .get_fault_info = exynos_sysmmu_v5_get_fault_info ,
315
363
};
316
364
317
365
/* SysMMU v7: VM capable register set */
@@ -324,6 +372,8 @@ static const struct sysmmu_variant sysmmu_v7_vm_variant = {
324
372
.flush_end = 0x8024 ,
325
373
.int_status = 0x60 ,
326
374
.int_clear = 0x64 ,
375
+
376
+ .get_fault_info = exynos_sysmmu_v5_get_fault_info ,
327
377
};
328
378
329
379
static struct exynos_iommu_domain * to_exynos_domain (struct iommu_domain * dom )
@@ -453,68 +503,56 @@ static void __sysmmu_get_version(struct sysmmu_drvdata *data)
453
503
}
454
504
455
505
static void show_fault_information (struct sysmmu_drvdata * data ,
456
- const struct sysmmu_fault_info * finfo ,
457
- sysmmu_iova_t fault_addr )
506
+ const struct sysmmu_fault * fault )
458
507
{
459
508
sysmmu_pte_t * ent ;
460
509
461
- dev_err (data -> sysmmu , "%s: %s FAULT occurred at %#x\n" ,
462
- dev_name (data -> master ), finfo -> name , fault_addr );
510
+ dev_err (data -> sysmmu , "%s: [%s] %s FAULT occurred at %#x\n" ,
511
+ dev_name (data -> master ),
512
+ fault -> type == IOMMU_FAULT_READ ? "READ" : "WRITE" ,
513
+ fault -> name , fault -> addr );
463
514
dev_dbg (data -> sysmmu , "Page table base: %pa\n" , & data -> pgtable );
464
- ent = section_entry (phys_to_virt (data -> pgtable ), fault_addr );
515
+ ent = section_entry (phys_to_virt (data -> pgtable ), fault -> addr );
465
516
dev_dbg (data -> sysmmu , "\tLv1 entry: %#x\n" , * ent );
466
517
if (lv1ent_page (ent )) {
467
- ent = page_entry (ent , fault_addr );
518
+ ent = page_entry (ent , fault -> addr );
468
519
dev_dbg (data -> sysmmu , "\t Lv2 entry: %#x\n" , * ent );
469
520
}
470
521
}
471
522
472
523
static irqreturn_t exynos_sysmmu_irq (int irq , void * dev_id )
473
524
{
474
- /* SYSMMU is in blocked state when interrupt occurred. */
475
525
struct sysmmu_drvdata * data = dev_id ;
476
- const struct sysmmu_fault_info * finfo ;
477
- unsigned int i , n , itype ;
478
- sysmmu_iova_t fault_addr ;
526
+ unsigned int itype ;
527
+ struct sysmmu_fault fault ;
479
528
int ret = - ENOSYS ;
480
529
481
530
WARN_ON (!data -> active );
482
531
483
- if (MMU_MAJ_VER (data -> version ) < 5 ) {
484
- finfo = sysmmu_faults ;
485
- n = ARRAY_SIZE (sysmmu_faults );
486
- } else {
487
- finfo = sysmmu_v5_faults ;
488
- n = ARRAY_SIZE (sysmmu_v5_faults );
489
- }
490
-
491
532
spin_lock (& data -> lock );
492
-
493
533
clk_enable (data -> clk_master );
494
534
495
535
itype = __ffs (readl (SYSMMU_REG (data , int_status )));
496
- for (i = 0 ; i < n ; i ++ , finfo ++ )
497
- if (finfo -> bit == itype )
498
- break ;
499
- /* unknown/unsupported fault */
500
- BUG_ON (i == n );
501
-
502
- /* print debug message */
503
- fault_addr = readl (data -> sfrbase + finfo -> addr_reg );
504
- show_fault_information (data , finfo , fault_addr );
505
-
506
- if (data -> domain )
507
- ret = report_iommu_fault (& data -> domain -> domain ,
508
- data -> master , fault_addr , finfo -> type );
509
- /* fault is not recovered by fault handler */
510
- BUG_ON (ret != 0 );
536
+ ret = data -> variant -> get_fault_info (data , itype , & fault );
537
+ if (ret ) {
538
+ dev_err (data -> sysmmu , "Unhandled interrupt bit %u\n" , itype );
539
+ goto out ;
540
+ }
541
+ show_fault_information (data , & fault );
511
542
543
+ if (data -> domain ) {
544
+ ret = report_iommu_fault (& data -> domain -> domain , data -> master ,
545
+ fault .addr , fault .type );
546
+ }
547
+ if (ret )
548
+ panic ("Unrecoverable System MMU Fault!" );
549
+
550
+ out :
512
551
writel (1 << itype , SYSMMU_REG (data , int_clear ));
513
552
553
+ /* SysMMU is in blocked state when interrupt occurred */
514
554
sysmmu_unblock (data );
515
-
516
555
clk_disable (data -> clk_master );
517
-
518
556
spin_unlock (& data -> lock );
519
557
520
558
return IRQ_HANDLED ;
0 commit comments