summaryrefslogtreecommitdiff
path: root/drivers/iommu/io-pgtable-dart.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/iommu/io-pgtable-dart.c')
-rw-r--r--drivers/iommu/io-pgtable-dart.c65
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);