Skip to content

Commit 746bc9b

Browse files
committed
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 071ca5c commit 746bc9b

2 files changed

Lines changed: 168 additions & 8 deletions

File tree

drivers/iommu/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,7 @@ config APPLE_DART
305305
depends on !GENERIC_ATOMIC64 # for IOMMU_IO_PGTABLE_DART
306306
select IOMMU_API
307307
select IOMMU_IO_PGTABLE_DART
308+
select OF_IOMMU
308309
default ARCH_APPLE
309310
help
310311
Support for Apple DART (Device Address Resolution Table) IOMMUs

drivers/iommu/apple-dart.c

Lines changed: 167 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,9 @@ struct apple_dart {
225225

226226
u32 save_tcr[DART_MAX_STREAMS];
227227
u32 save_ttbr[DART_MAX_STREAMS][DART_MAX_TTBR];
228+
229+
u64 *locked_ttbr[DART_MAX_STREAMS][DART_MAX_TTBR];
230+
u64 *shadow_ttbr[DART_MAX_STREAMS][DART_MAX_TTBR];
228231
};
229232

230233
/*
@@ -371,6 +374,89 @@ apple_dart_hw_clear_all_ttbrs(struct apple_dart_stream_map *stream_map)
371374
apple_dart_hw_clear_ttbr(stream_map, i);
372375
}
373376

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

580+
581+
if (stream_map.dart->locked)
582+
apple_dart_hw_sync_locked(&stream_map);
583+
494584
stream_map.dart->hw->invalidate_tlb(&stream_map);
495585
}
496586
}
@@ -559,17 +649,62 @@ apple_dart_setup_translation(struct apple_dart_domain *domain,
559649
struct io_pgtable_cfg *pgtbl_cfg =
560650
&io_pgtable_ops_to_pgtable(domain->pgtbl_ops)->cfg;
561651

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

568-
apple_dart_hw_enable_translation(stream_map);
667+
apple_dart_hw_enable_translation(stream_map);
668+
}
569669
stream_map->dart->hw->invalidate_tlb(stream_map);
570670
}
571671

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

738+
if (dart->locked) {
739+
unsigned long *sidmap;
740+
int sid;
741+
u32 ttbr;
742+
743+
/* Locked DARTs can only have a single stream bound */
744+
sidmap = cfg->stream_maps[0].sidmap;
745+
sid = find_first_bit(sidmap, dart->num_streams);
746+
747+
WARN_ON((sid < 0) || bitmap_weight(sidmap, dart->num_streams) > 1);
748+
ttbr = readl(dart->regs + DART_TTBR(dart, sid, 0));
749+
750+
WARN_ON(!(ttbr & dart->hw->ttbr_valid));
751+
}
752+
603753
dart_domain->pgtbl_ops = alloc_io_pgtable_ops(dart->hw->fmt, &pgtbl_cfg,
604754
&dart_domain->domain);
605755
if (!dart_domain->pgtbl_ops) {
@@ -615,6 +765,7 @@ static int apple_dart_finalize_domain(struct apple_dart_domain *dart_domain,
615765

616766
dart_domain->finalized = true;
617767

768+
ret = apple_dart_setup_resv_locked(&dart_domain->domain, dev, dart->pgsize);
618769
done:
619770
mutex_unlock(&dart_domain->init_lock);
620771
return ret;
@@ -663,7 +814,7 @@ static int apple_dart_attach_dev_paging(struct iommu_domain *domain,
663814
struct apple_dart_master_cfg *cfg = dev_iommu_priv_get(dev);
664815
struct apple_dart_domain *dart_domain = to_dart_domain(domain);
665816

666-
ret = apple_dart_finalize_domain(dart_domain, cfg);
817+
ret = apple_dart_finalize_domain(dart_domain, dev, cfg);
667818
if (ret)
668819
return ret;
669820

@@ -743,8 +894,16 @@ static struct iommu_device *apple_dart_probe_device(struct device *dev)
743894

744895
static void apple_dart_release_device(struct device *dev)
745896
{
897+
int i, j;
898+
struct apple_dart_stream_map *stream_map;
746899
struct apple_dart_master_cfg *cfg = dev_iommu_priv_get(dev);
747900

901+
for_each_stream_map(j, cfg, stream_map) {
902+
if (stream_map->dart->locked)
903+
for (i = 0; i < stream_map->dart->hw->ttbr_count; ++i)
904+
apple_dart_hw_clear_locked_ttbr(stream_map, i);
905+
}
906+
748907
kfree(cfg);
749908
}
750909

@@ -762,7 +921,7 @@ static struct iommu_domain *apple_dart_domain_alloc_paging(struct device *dev)
762921
struct apple_dart_master_cfg *cfg = dev_iommu_priv_get(dev);
763922
int ret;
764923

765-
ret = apple_dart_finalize_domain(dart_domain, cfg);
924+
ret = apple_dart_finalize_domain(dart_domain, dev, cfg);
766925
if (ret) {
767926
kfree(dart_domain);
768927
return ERR_PTR(ret);

0 commit comments

Comments
 (0)