Skip to content

Commit d207f47

Browse files
jannaumarcan
authored andcommitted
iommu: apple-dart: Install IOMMU_RESV_TRANSLATED mappings
The iommus for the display processors on Apple silicon machines have locked TTBR registers. To support iommu domain switching use a shadow L1 page table and sync it on flush to the HW L1 table. TODO: investigate if it's possible / necessary to optimize the syncing Signed-off-by: Janne Grunau <j@jannau.net>
1 parent 1de5894 commit d207f47

2 files changed

Lines changed: 167 additions & 7 deletions

File tree

drivers/iommu/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,7 @@ config APPLE_DART
308308
depends on !GENERIC_ATOMIC64 # for IOMMU_IO_PGTABLE_DART
309309
select IOMMU_API
310310
select IOMMU_IO_PGTABLE_DART
311+
select OF_IOMMU
311312
default ARCH_APPLE
312313
help
313314
Support for Apple DART (Device Address Resolution Table) IOMMUs

drivers/iommu/apple-dart.c

Lines changed: 166 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,9 @@ struct apple_dart {
226226

227227
u32 save_tcr[DART_MAX_STREAMS];
228228
u32 save_ttbr[DART_MAX_STREAMS][DART_MAX_TTBR];
229+
230+
u64 *locked_ttbr[DART_MAX_STREAMS][DART_MAX_TTBR];
231+
u64 *shadow_ttbr[DART_MAX_STREAMS][DART_MAX_TTBR];
229232
};
230233

231234
/*
@@ -372,6 +375,89 @@ apple_dart_hw_clear_all_ttbrs(struct apple_dart_stream_map *stream_map)
372375
apple_dart_hw_clear_ttbr(stream_map, i);
373376
}
374377

378+
static int
379+
apple_dart_hw_set_locked_ttbr(struct apple_dart_stream_map *stream_map, u8 idx,
380+
phys_addr_t paddr)
381+
{
382+
struct apple_dart *dart = stream_map->dart;
383+
int sid;
384+
385+
WARN_ON(!dart->locked);
386+
WARN_ON(paddr & ((1 << dart->hw->ttbr_shift) - 1));
387+
for_each_set_bit(sid, stream_map->sidmap, dart->num_streams) {
388+
u32 ttbr;
389+
phys_addr_t phys;
390+
u64 *l1_tbl, *l1_shadow;
391+
392+
ttbr = readl(dart->regs + DART_TTBR(dart, sid, idx));
393+
394+
WARN_ON(!(ttbr & dart->hw->ttbr_valid));
395+
ttbr &= ~dart->hw->ttbr_valid;
396+
397+
if (dart->hw->ttbr_addr_field_shift)
398+
ttbr >>= dart->hw->ttbr_addr_field_shift;
399+
phys = ((phys_addr_t) ttbr) << dart->hw->ttbr_shift;
400+
401+
l1_tbl = devm_memremap(dart->dev, phys, dart->pgsize,
402+
MEMREMAP_WB);
403+
if (!l1_tbl)
404+
return -ENOMEM;
405+
l1_shadow = devm_memremap(dart->dev, paddr, dart->pgsize,
406+
MEMREMAP_WB);
407+
if (!l1_shadow)
408+
return -ENOMEM;
409+
410+
dart->locked_ttbr[sid][idx] = l1_tbl;
411+
dart->shadow_ttbr[sid][idx] = l1_shadow;
412+
}
413+
414+
return 0;
415+
}
416+
417+
static int
418+
apple_dart_hw_clear_locked_ttbr(struct apple_dart_stream_map *stream_map,
419+
u8 idx)
420+
{
421+
struct apple_dart *dart = stream_map->dart;
422+
int sid;
423+
424+
WARN_ON(!dart->locked);
425+
for_each_set_bit(sid, stream_map->sidmap, dart->num_streams) {
426+
/* TODO: locked L1 table might need to be restored to boot state */
427+
if (dart->locked_ttbr[sid][idx]) {
428+
memset(dart->locked_ttbr[sid][idx], 0, dart->pgsize);
429+
devm_memunmap(dart->dev, dart->locked_ttbr[sid][idx]);
430+
}
431+
dart->locked_ttbr[sid][idx] = NULL;
432+
if (dart->shadow_ttbr[sid][idx])
433+
devm_memunmap(dart->dev, dart->shadow_ttbr[sid][idx]);
434+
dart->shadow_ttbr[sid][idx] = NULL;
435+
}
436+
437+
return 0;
438+
}
439+
440+
static int
441+
apple_dart_hw_sync_locked(struct apple_dart_stream_map *stream_map)
442+
{
443+
struct apple_dart *dart = stream_map->dart;
444+
int sid;
445+
446+
WARN_ON(!dart->locked);
447+
for_each_set_bit(sid, stream_map->sidmap, dart->num_streams) {
448+
for (int idx = 0; idx < dart->hw->ttbr_count; idx++) {
449+
u64 *ttbrep = dart->locked_ttbr[sid][idx];
450+
u64 *ptep = dart->shadow_ttbr[sid][idx];
451+
if (!ttbrep || !ptep)
452+
continue;
453+
for (int entry = 0; entry < dart->pgsize / sizeof(*ptep); entry++)
454+
ttbrep[entry] = ptep[entry];
455+
}
456+
}
457+
458+
return 0;
459+
}
460+
375461
static int
376462
apple_dart_t8020_hw_stream_command(struct apple_dart_stream_map *stream_map,
377463
u32 command)
@@ -490,6 +576,10 @@ static void apple_dart_domain_flush_tlb(struct apple_dart_domain *domain)
490576
for (j = 0; j < BITS_TO_LONGS(stream_map.dart->num_streams); j++)
491577
stream_map.sidmap[j] = atomic_long_read(&domain_stream_map->sidmap[j]);
492578

579+
580+
if (stream_map.dart->locked)
581+
apple_dart_hw_sync_locked(&stream_map);
582+
493583
stream_map.dart->hw->invalidate_tlb(&stream_map);
494584
}
495585
}
@@ -557,17 +647,62 @@ apple_dart_setup_translation(struct apple_dart_domain *domain,
557647
struct io_pgtable_cfg *pgtbl_cfg =
558648
&io_pgtable_ops_to_pgtable(domain->pgtbl_ops)->cfg;
559649

560-
for (i = 0; i < pgtbl_cfg->apple_dart_cfg.n_ttbrs; ++i)
561-
apple_dart_hw_set_ttbr(stream_map, i,
562-
pgtbl_cfg->apple_dart_cfg.ttbr[i]);
563-
for (; i < stream_map->dart->hw->ttbr_count; ++i)
564-
apple_dart_hw_clear_ttbr(stream_map, i);
650+
/* Locked DARTs are set up by the bootloader. */
651+
if (stream_map->dart->locked) {
652+
for (i = 0; i < pgtbl_cfg->apple_dart_cfg.n_ttbrs; ++i)
653+
apple_dart_hw_set_locked_ttbr(stream_map, i,
654+
pgtbl_cfg->apple_dart_cfg.ttbr[i]);
655+
for (; i < stream_map->dart->hw->ttbr_count; ++i)
656+
apple_dart_hw_clear_locked_ttbr(stream_map, i);
657+
apple_dart_hw_sync_locked(stream_map);
658+
} else {
659+
for (i = 0; i < pgtbl_cfg->apple_dart_cfg.n_ttbrs; ++i)
660+
apple_dart_hw_set_ttbr(stream_map, i,
661+
pgtbl_cfg->apple_dart_cfg.ttbr[i]);
662+
for (; i < stream_map->dart->hw->ttbr_count; ++i)
663+
apple_dart_hw_clear_ttbr(stream_map, i);
565664

566-
apple_dart_hw_enable_translation(stream_map);
665+
apple_dart_hw_enable_translation(stream_map);
666+
}
567667
stream_map->dart->hw->invalidate_tlb(stream_map);
568668
}
569669

670+
static int apple_dart_setup_resv_locked(struct iommu_domain *domain,
671+
struct device *dev, size_t pgsize)
672+
{
673+
struct iommu_resv_region *region;
674+
LIST_HEAD(resv_regions);
675+
int ret = 0;
676+
677+
of_iommu_get_resv_regions(dev, &resv_regions);
678+
list_for_each_entry(region, &resv_regions, list) {
679+
size_t mapped = 0;
680+
681+
/* Only map translated reserved regions */
682+
if (region->type != IOMMU_RESV_TRANSLATED)
683+
continue;
684+
685+
while (mapped < region->length) {
686+
phys_addr_t paddr = region->start + mapped;
687+
unsigned long iova = region->dva + mapped;
688+
size_t length = region->length - mapped;
689+
size_t pgcount = length / pgsize;
690+
691+
ret = apple_dart_map_pages(domain, iova,
692+
paddr, pgsize, pgcount,
693+
region->prot, GFP_KERNEL, &mapped);
694+
695+
if (ret)
696+
goto end_put;
697+
}
698+
}
699+
end_put:
700+
iommu_put_resv_regions(dev, &resv_regions);
701+
return ret;
702+
}
703+
570704
static int apple_dart_finalize_domain(struct iommu_domain *domain,
705+
struct device *dev,
571706
struct apple_dart_master_cfg *cfg)
572707
{
573708
struct apple_dart_domain *dart_domain = to_dart_domain(domain);
@@ -596,6 +731,21 @@ static int apple_dart_finalize_domain(struct iommu_domain *domain,
596731
.iommu_dev = dart->dev,
597732
};
598733

734+
if (dart->locked) {
735+
unsigned long *sidmap;
736+
int sid;
737+
u32 ttbr;
738+
739+
/* Locked DARTs can only have a single stream bound */
740+
sidmap = cfg->stream_maps[0].sidmap;
741+
sid = find_first_bit(sidmap, dart->num_streams);
742+
743+
WARN_ON((sid < 0) || bitmap_weight(sidmap, dart->num_streams) > 1);
744+
ttbr = readl(dart->regs + DART_TTBR(dart, sid, 0));
745+
746+
WARN_ON(!(ttbr & dart->hw->ttbr_valid));
747+
}
748+
599749
dart_domain->pgtbl_ops =
600750
alloc_io_pgtable_ops(dart->hw->fmt, &pgtbl_cfg, domain);
601751
if (!dart_domain->pgtbl_ops) {
@@ -610,6 +760,7 @@ static int apple_dart_finalize_domain(struct iommu_domain *domain,
610760

611761
dart_domain->finalized = true;
612762

763+
ret = apple_dart_setup_resv_locked(domain, dev, dart->pgsize);
613764
done:
614765
mutex_unlock(&dart_domain->init_lock);
615766
return ret;
@@ -667,7 +818,7 @@ static int apple_dart_attach_dev(struct iommu_domain *domain,
667818
domain->type != IOMMU_DOMAIN_UNMANAGED)
668819
return -EINVAL;
669820

670-
ret = apple_dart_finalize_domain(domain, cfg);
821+
ret = apple_dart_finalize_domain(domain, dev, cfg);
671822
if (ret)
672823
return ret;
673824

@@ -712,8 +863,16 @@ static struct iommu_device *apple_dart_probe_device(struct device *dev)
712863

713864
static void apple_dart_release_device(struct device *dev)
714865
{
866+
int i, j;
867+
struct apple_dart_stream_map *stream_map;
715868
struct apple_dart_master_cfg *cfg = dev_iommu_priv_get(dev);
716869

870+
for_each_stream_map(j, cfg, stream_map) {
871+
if (stream_map->dart->locked)
872+
for (i = 0; i < stream_map->dart->hw->ttbr_count; ++i)
873+
apple_dart_hw_clear_locked_ttbr(stream_map, i);
874+
}
875+
717876
kfree(cfg);
718877
}
719878

0 commit comments

Comments
 (0)