Skip to content

Commit bfee0cf

Browse files
Josue Albarranjoergroedel
authored andcommitted
iommu/omap: Use DMA-API for performing cache flushes
The OMAP IOMMU driver was using ARM assembly code directly for flushing the MMU page table entries from the caches. This caused MMU faults on OMAP4 (Cortex-A9 based SoCs) as L2 caches were not handled due to the presence of a PL310 L2 Cache Controller. These faults were however not seen on OMAP5/DRA7 SoCs (Cortex-A15 based SoCs). The OMAP IOMMU driver is adapted to use the DMA Streaming API instead now to flush the page table/directory table entries from the CPU caches. This ensures that the devices always see the updated page table entries. The outer caches are now addressed automatically with the usage of the DMA API. Signed-off-by: Josue Albarran <[email protected]> Acked-by: Suman Anna <[email protected]> Signed-off-by: Joerg Roedel <[email protected]>
1 parent 159d3e3 commit bfee0cf

File tree

2 files changed

+80
-44
lines changed

2 files changed

+80
-44
lines changed

drivers/iommu/omap-iommu.c

Lines changed: 79 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
* published by the Free Software Foundation.
1212
*/
1313

14+
#include <linux/dma-mapping.h>
1415
#include <linux/err.h>
1516
#include <linux/slab.h>
1617
#include <linux/interrupt.h>
@@ -29,8 +30,6 @@
2930
#include <linux/regmap.h>
3031
#include <linux/mfd/syscon.h>
3132

32-
#include <asm/cacheflush.h>
33-
3433
#include <linux/platform_data/iommu-omap.h>
3534

3635
#include "omap-iopgtable.h"
@@ -454,36 +453,35 @@ static void flush_iotlb_all(struct omap_iommu *obj)
454453
/*
455454
* H/W pagetable operations
456455
*/
457-
static void flush_iopgd_range(u32 *first, u32 *last)
456+
static void flush_iopte_range(struct device *dev, dma_addr_t dma,
457+
unsigned long offset, int num_entries)
458458
{
459-
/* FIXME: L2 cache should be taken care of if it exists */
460-
do {
461-
asm("mcr p15, 0, %0, c7, c10, 1 @ flush_pgd"
462-
: : "r" (first));
463-
first += L1_CACHE_BYTES / sizeof(*first);
464-
} while (first <= last);
465-
}
459+
size_t size = num_entries * sizeof(u32);
466460

467-
static void flush_iopte_range(u32 *first, u32 *last)
468-
{
469-
/* FIXME: L2 cache should be taken care of if it exists */
470-
do {
471-
asm("mcr p15, 0, %0, c7, c10, 1 @ flush_pte"
472-
: : "r" (first));
473-
first += L1_CACHE_BYTES / sizeof(*first);
474-
} while (first <= last);
461+
dma_sync_single_range_for_device(dev, dma, offset, size, DMA_TO_DEVICE);
475462
}
476463

477-
static void iopte_free(u32 *iopte)
464+
static void iopte_free(struct omap_iommu *obj, u32 *iopte, bool dma_valid)
478465
{
466+
dma_addr_t pt_dma;
467+
479468
/* Note: freed iopte's must be clean ready for re-use */
480-
if (iopte)
469+
if (iopte) {
470+
if (dma_valid) {
471+
pt_dma = virt_to_phys(iopte);
472+
dma_unmap_single(obj->dev, pt_dma, IOPTE_TABLE_SIZE,
473+
DMA_TO_DEVICE);
474+
}
475+
481476
kmem_cache_free(iopte_cachep, iopte);
477+
}
482478
}
483479

484-
static u32 *iopte_alloc(struct omap_iommu *obj, u32 *iopgd, u32 da)
480+
static u32 *iopte_alloc(struct omap_iommu *obj, u32 *iopgd,
481+
dma_addr_t *pt_dma, u32 da)
485482
{
486483
u32 *iopte;
484+
unsigned long offset = iopgd_index(da) * sizeof(da);
487485

488486
/* a table has already existed */
489487
if (*iopgd)
@@ -500,18 +498,38 @@ static u32 *iopte_alloc(struct omap_iommu *obj, u32 *iopgd, u32 da)
500498
if (!iopte)
501499
return ERR_PTR(-ENOMEM);
502500

501+
*pt_dma = dma_map_single(obj->dev, iopte, IOPTE_TABLE_SIZE,
502+
DMA_TO_DEVICE);
503+
if (dma_mapping_error(obj->dev, *pt_dma)) {
504+
dev_err(obj->dev, "DMA map error for L2 table\n");
505+
iopte_free(obj, iopte, false);
506+
return ERR_PTR(-ENOMEM);
507+
}
508+
509+
/*
510+
* we rely on dma address and the physical address to be
511+
* the same for mapping the L2 table
512+
*/
513+
if (WARN_ON(*pt_dma != virt_to_phys(iopte))) {
514+
dev_err(obj->dev, "DMA translation error for L2 table\n");
515+
dma_unmap_single(obj->dev, *pt_dma, IOPTE_TABLE_SIZE,
516+
DMA_TO_DEVICE);
517+
iopte_free(obj, iopte, false);
518+
return ERR_PTR(-ENOMEM);
519+
}
520+
503521
*iopgd = virt_to_phys(iopte) | IOPGD_TABLE;
504-
flush_iopgd_range(iopgd, iopgd);
505522

523+
flush_iopte_range(obj->dev, obj->pd_dma, offset, 1);
506524
dev_vdbg(obj->dev, "%s: a new pte:%p\n", __func__, iopte);
507525
} else {
508526
/* We raced, free the reduniovant table */
509-
iopte_free(iopte);
527+
iopte_free(obj, iopte, false);
510528
}
511529

512530
pte_ready:
513531
iopte = iopte_offset(iopgd, da);
514-
532+
*pt_dma = virt_to_phys(iopte);
515533
dev_vdbg(obj->dev,
516534
"%s: da:%08x pgd:%p *pgd:%08x pte:%p *pte:%08x\n",
517535
__func__, da, iopgd, *iopgd, iopte, *iopte);
@@ -522,6 +540,7 @@ static u32 *iopte_alloc(struct omap_iommu *obj, u32 *iopgd, u32 da)
522540
static int iopgd_alloc_section(struct omap_iommu *obj, u32 da, u32 pa, u32 prot)
523541
{
524542
u32 *iopgd = iopgd_offset(obj, da);
543+
unsigned long offset = iopgd_index(da) * sizeof(da);
525544

526545
if ((da | pa) & ~IOSECTION_MASK) {
527546
dev_err(obj->dev, "%s: %08x:%08x should aligned on %08lx\n",
@@ -530,13 +549,14 @@ static int iopgd_alloc_section(struct omap_iommu *obj, u32 da, u32 pa, u32 prot)
530549
}
531550

532551
*iopgd = (pa & IOSECTION_MASK) | prot | IOPGD_SECTION;
533-
flush_iopgd_range(iopgd, iopgd);
552+
flush_iopte_range(obj->dev, obj->pd_dma, offset, 1);
534553
return 0;
535554
}
536555

537556
static int iopgd_alloc_super(struct omap_iommu *obj, u32 da, u32 pa, u32 prot)
538557
{
539558
u32 *iopgd = iopgd_offset(obj, da);
559+
unsigned long offset = iopgd_index(da) * sizeof(da);
540560
int i;
541561

542562
if ((da | pa) & ~IOSUPER_MASK) {
@@ -547,20 +567,22 @@ static int iopgd_alloc_super(struct omap_iommu *obj, u32 da, u32 pa, u32 prot)
547567

548568
for (i = 0; i < 16; i++)
549569
*(iopgd + i) = (pa & IOSUPER_MASK) | prot | IOPGD_SUPER;
550-
flush_iopgd_range(iopgd, iopgd + 15);
570+
flush_iopte_range(obj->dev, obj->pd_dma, offset, 16);
551571
return 0;
552572
}
553573

554574
static int iopte_alloc_page(struct omap_iommu *obj, u32 da, u32 pa, u32 prot)
555575
{
556576
u32 *iopgd = iopgd_offset(obj, da);
557-
u32 *iopte = iopte_alloc(obj, iopgd, da);
577+
dma_addr_t pt_dma;
578+
u32 *iopte = iopte_alloc(obj, iopgd, &pt_dma, da);
579+
unsigned long offset = iopte_index(da) * sizeof(da);
558580

559581
if (IS_ERR(iopte))
560582
return PTR_ERR(iopte);
561583

562584
*iopte = (pa & IOPAGE_MASK) | prot | IOPTE_SMALL;
563-
flush_iopte_range(iopte, iopte);
585+
flush_iopte_range(obj->dev, pt_dma, offset, 1);
564586

565587
dev_vdbg(obj->dev, "%s: da:%08x pa:%08x pte:%p *pte:%08x\n",
566588
__func__, da, pa, iopte, *iopte);
@@ -571,7 +593,9 @@ static int iopte_alloc_page(struct omap_iommu *obj, u32 da, u32 pa, u32 prot)
571593
static int iopte_alloc_large(struct omap_iommu *obj, u32 da, u32 pa, u32 prot)
572594
{
573595
u32 *iopgd = iopgd_offset(obj, da);
574-
u32 *iopte = iopte_alloc(obj, iopgd, da);
596+
dma_addr_t pt_dma;
597+
u32 *iopte = iopte_alloc(obj, iopgd, &pt_dma, da);
598+
unsigned long offset = iopte_index(da) * sizeof(da);
575599
int i;
576600

577601
if ((da | pa) & ~IOLARGE_MASK) {
@@ -585,7 +609,7 @@ static int iopte_alloc_large(struct omap_iommu *obj, u32 da, u32 pa, u32 prot)
585609

586610
for (i = 0; i < 16; i++)
587611
*(iopte + i) = (pa & IOLARGE_MASK) | prot | IOPTE_LARGE;
588-
flush_iopte_range(iopte, iopte + 15);
612+
flush_iopte_range(obj->dev, pt_dma, offset, 16);
589613
return 0;
590614
}
591615

@@ -674,6 +698,9 @@ static size_t iopgtable_clear_entry_core(struct omap_iommu *obj, u32 da)
674698
size_t bytes;
675699
u32 *iopgd = iopgd_offset(obj, da);
676700
int nent = 1;
701+
dma_addr_t pt_dma;
702+
unsigned long pd_offset = iopgd_index(da) * sizeof(da);
703+
unsigned long pt_offset = iopte_index(da) * sizeof(da);
677704

678705
if (!*iopgd)
679706
return 0;
@@ -690,7 +717,8 @@ static size_t iopgtable_clear_entry_core(struct omap_iommu *obj, u32 da)
690717
}
691718
bytes *= nent;
692719
memset(iopte, 0, nent * sizeof(*iopte));
693-
flush_iopte_range(iopte, iopte + (nent - 1) * sizeof(*iopte));
720+
pt_dma = virt_to_phys(iopte);
721+
flush_iopte_range(obj->dev, pt_dma, pt_offset, nent);
694722

695723
/*
696724
* do table walk to check if this table is necessary or not
@@ -700,7 +728,7 @@ static size_t iopgtable_clear_entry_core(struct omap_iommu *obj, u32 da)
700728
if (iopte[i])
701729
goto out;
702730

703-
iopte_free(iopte);
731+
iopte_free(obj, iopte, true);
704732
nent = 1; /* for the next L1 entry */
705733
} else {
706734
bytes = IOPGD_SIZE;
@@ -712,7 +740,7 @@ static size_t iopgtable_clear_entry_core(struct omap_iommu *obj, u32 da)
712740
bytes *= nent;
713741
}
714742
memset(iopgd, 0, nent * sizeof(*iopgd));
715-
flush_iopgd_range(iopgd, iopgd + (nent - 1) * sizeof(*iopgd));
743+
flush_iopte_range(obj->dev, obj->pd_dma, pd_offset, nent);
716744
out:
717745
return bytes;
718746
}
@@ -738,6 +766,7 @@ static size_t iopgtable_clear_entry(struct omap_iommu *obj, u32 da)
738766

739767
static void iopgtable_clear_entry_all(struct omap_iommu *obj)
740768
{
769+
unsigned long offset;
741770
int i;
742771

743772
spin_lock(&obj->page_table_lock);
@@ -748,15 +777,16 @@ static void iopgtable_clear_entry_all(struct omap_iommu *obj)
748777

749778
da = i << IOPGD_SHIFT;
750779
iopgd = iopgd_offset(obj, da);
780+
offset = iopgd_index(da) * sizeof(da);
751781

752782
if (!*iopgd)
753783
continue;
754784

755785
if (iopgd_is_table(*iopgd))
756-
iopte_free(iopte_offset(iopgd, 0));
786+
iopte_free(obj, iopte_offset(iopgd, 0), true);
757787

758788
*iopgd = 0;
759-
flush_iopgd_range(iopgd, iopgd);
789+
flush_iopte_range(obj->dev, obj->pd_dma, offset, 1);
760790
}
761791

762792
flush_iotlb_all(obj);
@@ -815,10 +845,18 @@ static int omap_iommu_attach(struct omap_iommu *obj, u32 *iopgd)
815845

816846
spin_lock(&obj->iommu_lock);
817847

848+
obj->pd_dma = dma_map_single(obj->dev, iopgd, IOPGD_TABLE_SIZE,
849+
DMA_TO_DEVICE);
850+
if (dma_mapping_error(obj->dev, obj->pd_dma)) {
851+
dev_err(obj->dev, "DMA map error for L1 table\n");
852+
err = -ENOMEM;
853+
goto out_err;
854+
}
855+
818856
obj->iopgd = iopgd;
819857
err = iommu_enable(obj);
820858
if (err)
821-
goto err_enable;
859+
goto out_err;
822860
flush_iotlb_all(obj);
823861

824862
spin_unlock(&obj->iommu_lock);
@@ -827,7 +865,7 @@ static int omap_iommu_attach(struct omap_iommu *obj, u32 *iopgd)
827865

828866
return 0;
829867

830-
err_enable:
868+
out_err:
831869
spin_unlock(&obj->iommu_lock);
832870

833871
return err;
@@ -844,7 +882,10 @@ static void omap_iommu_detach(struct omap_iommu *obj)
844882

845883
spin_lock(&obj->iommu_lock);
846884

885+
dma_unmap_single(obj->dev, obj->pd_dma, IOPGD_TABLE_SIZE,
886+
DMA_TO_DEVICE);
847887
iommu_disable(obj);
888+
obj->pd_dma = 0;
848889
obj->iopgd = NULL;
849890

850891
spin_unlock(&obj->iommu_lock);
@@ -1008,11 +1049,6 @@ static struct platform_driver omap_iommu_driver = {
10081049
},
10091050
};
10101051

1011-
static void iopte_cachep_ctor(void *iopte)
1012-
{
1013-
clean_dcache_area(iopte, IOPTE_TABLE_SIZE);
1014-
}
1015-
10161052
static u32 iotlb_init_entry(struct iotlb_entry *e, u32 da, u32 pa, int pgsz)
10171053
{
10181054
memset(e, 0, sizeof(*e));
@@ -1159,7 +1195,6 @@ static struct iommu_domain *omap_iommu_domain_alloc(unsigned type)
11591195
if (WARN_ON(!IS_ALIGNED((long)omap_domain->pgtable, IOPGD_TABLE_SIZE)))
11601196
goto fail_align;
11611197

1162-
clean_dcache_area(omap_domain->pgtable, IOPGD_TABLE_SIZE);
11631198
spin_lock_init(&omap_domain->lock);
11641199

11651200
omap_domain->domain.geometry.aperture_start = 0;
@@ -1347,7 +1382,7 @@ static int __init omap_iommu_init(void)
13471382
of_node_put(np);
13481383

13491384
p = kmem_cache_create("iopte_cache", IOPTE_TABLE_SIZE, align, flags,
1350-
iopte_cachep_ctor);
1385+
NULL);
13511386
if (!p)
13521387
return -ENOMEM;
13531388
iopte_cachep = p;

drivers/iommu/omap-iommu.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ struct omap_iommu {
6161
*/
6262
u32 *iopgd;
6363
spinlock_t page_table_lock; /* protect iopgd */
64+
dma_addr_t pd_dma;
6465

6566
int nr_tlb_entries;
6667

0 commit comments

Comments
 (0)