diff options
Diffstat (limited to 'drivers/iommu/io-pgtable-dart.c')
-rw-r--r-- | drivers/iommu/io-pgtable-dart.c | 65 |
1 files changed, 58 insertions, 7 deletions
diff --git a/drivers/iommu/io-pgtable-dart.c b/drivers/iommu/io-pgtable-dart.c index 74b1ef2b96be..8459772bfefe 100644 --- a/drivers/iommu/io-pgtable-dart.c +++ b/drivers/iommu/io-pgtable-dart.c @@ -16,6 +16,8 @@ #include <linux/atomic.h> #include <linux/bitfield.h> #include <linux/bitops.h> +#include "linux/export.h" +#include <linux/io.h> #include <linux/io-pgtable.h> #include <linux/kernel.h> #include <linux/sizes.h> @@ -24,6 +26,8 @@ #include <asm/barrier.h> +#include "io-pgtable-dart.h" + #define DART1_MAX_ADDR_BITS 36 #define DART_MAX_TABLES 4 @@ -106,8 +110,7 @@ static phys_addr_t iopte_to_paddr(dart_iopte pte, return paddr; } -static void *__dart_alloc_pages(size_t size, gfp_t gfp, - struct io_pgtable_cfg *cfg) +static void *__dart_alloc_pages(size_t size, gfp_t gfp) { int order = get_order(size); struct page *p; @@ -262,7 +265,7 @@ static int dart_map_pages(struct io_pgtable_ops *ops, unsigned long iova, /* no L2 table present */ if (!pte) { - cptep = __dart_alloc_pages(tblsz, gfp, cfg); + cptep = __dart_alloc_pages(tblsz, gfp); if (!cptep) return -ENOMEM; @@ -363,6 +366,32 @@ static phys_addr_t dart_iova_to_phys(struct io_pgtable_ops *ops, return 0; } +int io_pgtable_dart_setup_locked(struct io_pgtable_ops *ops) +{ + void *l1tbl; + struct dart_io_pgtable *data = io_pgtable_ops_to_data(ops); + struct io_pgtable_cfg *cfg = &data->iop.cfg; + size_t size; + + if (!(cfg->quirks & IO_PGTABLE_QUIRK_APPLE_LOCKED)) + return 0; + + size = cfg->pgsize_bitmap; + l1tbl = devm_memremap(cfg->iommu_dev, cfg->apple_dart_cfg.ttbr[0], size, + MEMREMAP_WB); + if (!l1tbl) + return -ENOMEM; + + for (int entry = 0; entry < DART_PTES_PER_TABLE(data); entry++) + ((dart_iopte *)l1tbl)[entry] = ((dart_iopte *)data->pgd[0])[entry]; + + free_pages((unsigned long)data->pgd[0], get_order(DART_GRANULE(data))); + data->pgd[0] = l1tbl; + + return 0; +} +EXPORT_SYMBOL(io_pgtable_dart_setup_locked); + static struct dart_io_pgtable * dart_alloc_pgtable(struct io_pgtable_cfg *cfg) { @@ -418,30 +447,52 @@ apple_dart_alloc_pgtable(struct io_pgtable_cfg *cfg, void *cookie) cfg->apple_dart_cfg.n_ttbrs = 1 << data->tbl_bits; + /* Locked DARTs can not modify the TTBR registers. Allocate first a shadow + * page table so locked DARTs (disp0, dcp, dcpext*) can map their reserved + * memory regions. They will be later in io_pgtable_dart_setup_locked() + * copied to the locked L1 table. + */ + if (cfg->quirks & IO_PGTABLE_QUIRK_APPLE_LOCKED) { + if (cfg->apple_dart_cfg.n_ttbrs > 1) + goto out_free_data; + + data->pgd[0] = __dart_alloc_pages(DART_GRANULE(data), GFP_KERNEL); + if (!data->pgd[0]) + goto out_free_data; + + return &data->iop; + } + for (i = 0; i < cfg->apple_dart_cfg.n_ttbrs; ++i) { - data->pgd[i] = __dart_alloc_pages(DART_GRANULE(data), GFP_KERNEL, - cfg); + data->pgd[i] = __dart_alloc_pages(DART_GRANULE(data), GFP_KERNEL); if (!data->pgd[i]) - goto out_free_data; + goto out_free_pages; cfg->apple_dart_cfg.ttbr[i] = virt_to_phys(data->pgd[i]); } return &data->iop; -out_free_data: +out_free_pages: while (--i >= 0) free_pages((unsigned long)data->pgd[i], get_order(DART_GRANULE(data))); +out_free_data: kfree(data); return NULL; } static void apple_dart_free_pgtable(struct io_pgtable *iop) { + struct io_pgtable_cfg *cfg = &iop->cfg; struct dart_io_pgtable *data = io_pgtable_to_data(iop); dart_iopte *ptep, *end; int i; + if (cfg->quirks & IO_PGTABLE_QUIRK_APPLE_LOCKED) { + kfree(data); + return; + } + for (i = 0; i < (1 << data->tbl_bits) && data->pgd[i]; ++i) { ptep = data->pgd[i]; end = (void *)ptep + DART_GRANULE(data); |