SYSMMU driver update and support for Exynos 5433
Marek Szyprowski
maintainercover SYSMMU driver update and support for Exynos 5433
Hello,
This patchset updates Exynos SYSMMU (IOMMU) driver to make use of the new features in the IOMMU core (support for IOMMU_DOMAIN_DMA) and adds support for SYSMMU v5 controllers, which are available in Samsung Exynos 5433 SoCs. The driver has been also updated to compile and work on ARM64 architecture.
Best regards
Marek Szyprowski
Samsung R&D Institute Poland
Patch summary:
Marek Szyprowski (11):
iommu: exynos: rework iommu group initialization
iommu: exynos: add support for IOMMU_DOMAIN_DMA domain type
iommu: exynos: remove ARM-specific cache flush interface
iommu: exynos: simplify master clock operations
iommu: exynos: refactor code (no direct register access)
iommu: exynos: refactor fault handling code
iommu: exynos: refactor init config code
iommu: exynos: unify code for fldp cache invalidation
iommu: exynos: update device tree documentation
iommu: exynos: add support for v5 SYSMMU
iommu: exynos: add Maintainers entry for Exynos SYSMMU driver
.../devicetree/bindings/iommu/samsung,sysmmu.txt | 22 +- MAINTAINERS | 6 + drivers/iommu/Kconfig | 2 +- drivers/iommu/exynos-iommu.c | 514 ++++++++++++--------- 4 files changed, 318 insertions(+), 226 deletions(-)
Signature · 1 line
1.9.2
Marek Szyprowski
maintainerPATCH 1/11 iommu: exynos: rework iommu group initialization
This patch replaces custom code in add_device implementation with iommu_group_get_for_dev() call and provides the needed callback.
--- drivers/iommu/exynos-iommu.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-)
drivers/iommu/exynos-iommu.c +16 -11
@@ -1114,28 +1114,32 @@ static phys_addr_t exynos_iommu_iova_to_phys(struct iommu_domain *iommu_domain,return phys; } +static struct iommu_group *get_device_iommu_group (struct device *dev) +{ +struct iommu_group *group; + + group =iommu_group_get (dev); +if (!group) + group =iommu_group_alloc (); + +return group; +} +static int exynos_iommu_add_device (struct device *dev) {struct iommu_group *group; -int ret;if (!has_sysmmu (dev))return -ENODEV ; - group =iommu_group_get (dev); + group =iommu_group_get_for_dev (dev); -if (!group) { - group =iommu_group_alloc (); -if (IS_ERR (group)) { -dev_err (dev,"Failed to allocate IOMMU group\n" ); -return PTR_ERR (group); - } - } +if (IS_ERR (group)) +return PTR_ERR (group); - ret =iommu_group_add_device (group, dev);iommu_group_put (group); -return ret; +return 0 ; }static void exynos_iommu_remove_device (struct device *dev)
@@ -1182,6 +1186,7 @@ static struct iommu_ops exynos_iommu_ops = { .unmap = exynos_iommu_unmap, .map_sg = default_iommu_map_sg, .iova_to_phys = exynos_iommu_iova_to_phys, + .device_group = get_device_iommu_group, .add_device = exynos_iommu_add_device, .remove_device = exynos_iommu_remove_device, .pgsize_bitmap = SECT_SIZE | LPAGE_SIZE | SPAGE_SIZE,
Signature · 1 line
1.9.2
Marek Szyprowski
maintainerPATCH 2/11 iommu: exynos: add support for IOMMU_DOMAIN_DMA domain type
In reply to Marek SzyprowskiThis patch adds support for DMA domain type. Such domain have DMA cookie prepared and can be used by generic DMA-IOMMU glue layer.
--- drivers/iommu/exynos-iommu.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-)
drivers/iommu/exynos-iommu.c +15 -4
@@ -25,9 +25,9 @@#include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/slab.h> +#include <linux/dma-iommu.h> #include <asm/cacheflush.h> -#include <asm/dma-iommu.h> #include <asm/pgtable.h> typedef u32 sysmmu_iova_t ;
@@ -662,16 +662,21 @@ static struct iommu_domain *exynos_iommu_domain_alloc(unsigned type)struct exynos_iommu_domain *domain;int i; -if (type !=IOMMU_DOMAIN_UNMANAGED ) -return NULL ; domain =kzalloc (sizeof (*domain),GFP_KERNEL );if (!domain)return NULL ; +if (type ==IOMMU_DOMAIN_DMA ) { +if (iommu_get_dma_cookie (&domain->domain) !=0 ) + goto err_pgtable; + }else if (type !=IOMMU_DOMAIN_UNMANAGED ) { + goto err_pgtable; + } + domain->pgtable = (sysmmu_pte_t *)__get_free_pages (GFP_KERNEL ,2 );if (!domain->pgtable) - goto err_pgtable; + goto err_dma_cookie; domain->lv2entcnt = (short *)__get_free_pages (GFP_KERNEL | __GFP_ZERO,1 );if (!domain->lv2entcnt)
@@ -703,6 +708,9 @@ static struct iommu_domain *exynos_iommu_domain_alloc(unsigned type) err_counter:free_pages ((unsigned long )domain->pgtable,2 ); +err_dma_cookie: +if (type ==IOMMU_DOMAIN_DMA ) +iommu_put_dma_cookie (&domain->domain); err_pgtable:kfree (domain);return NULL ;
@@ -727,6 +735,9 @@ static void exynos_iommu_domain_free(struct iommu_domain *iommu_domain)spin_unlock_irqrestore (&domain->lock, flags); +if (iommu_domain->type ==IOMMU_DOMAIN_DMA ) +iommu_put_dma_cookie (iommu_domain); +for (i =0 ; i <NUM_LV1ENTRIES ; i++)if (lv1ent_page (domain->pgtable + i)) kmem_cache_free(lv2table_kmem_cache,
Signature · 1 line
1.9.2
Marek Szyprowski
maintainerPATCH 3/11 iommu: exynos: remove ARM-specific cache flush interface
In reply to Marek SzyprowskiThis patch replaces custom ARM-specific code for performing CPU cache flush operations with generic code based on DMA-mapping. Domain managing code is independent of particular SYSMMU device, so the first registered SYSMMU device is used for DMA-mapping calls. This simplification works fine because all SYSMMU controllers are in the same address space (where DMA address equals physical address) and the DMA-mapping calls are done mainly to flush CPU cache to make changes visible to SYSMMU controllers.
--- drivers/iommu/exynos-iommu.c | 74 +++++++++++++++++++++++++++++--------------- 1 file changed, 49 insertions(+), 25 deletions(-)
drivers/iommu/exynos-iommu.c +49 -25
@@ -27,9 +27,6 @@#include <linux/slab.h> #include <linux/dma-iommu.h> -#include <asm/cacheflush.h> -#include <asm/pgtable.h> -typedef u32 sysmmu_iova_t ;typedef u32 sysmmu_pte_t ;
@@ -83,6 +80,7 @@ static u32 lv2ent_offset(sysmmu_iova_t iova)return (iova >>SPAGE_ORDER ) & (NUM_LV2ENTRIES -1 ); } +#define LV1TABLE_SIZE (NUM_LV1ENTRIES * sizeof(sysmmu_pte_t))#define LV2TABLE_SIZE (NUM_LV2ENTRIES * sizeof(sysmmu_pte_t))#define SPAGES_PER_LPAGE (LPAGE_SIZE / SPAGE_SIZE)
@@ -134,6 +132,7 @@ static u32 lv2ent_offset(sysmmu_iova_t iova)#define has_sysmmu (dev) (dev->archdata.iommu != NULL) +static struct device *dma_dev;static struct kmem_cache *lv2table_kmem_cache;static sysmmu_pte_t *zero_lv2_table;#define ZERO_LV2LINK mk_lv1ent_page(virt_to_phys(zero_lv2_table))
@@ -650,16 +649,19 @@ static struct platform_driver exynos_sysmmu_driver __refdata = { } }; -static inline void pgtable_flush (void *vastart,void *vaend) +static inline void update_pte (sysmmu_pte_t *ent,sysmmu_pte_t val) { -dmac_flush_range (vastart, vaend); -outer_flush_range (virt_to_phys (vastart), -virt_to_phys (vaend)); +dma_sync_single_for_cpu (dma_dev,virt_to_phys (ent),sizeof (*ent), +DMA_TO_DEVICE ); + *ent = val; +dma_sync_single_for_device (dma_dev,virt_to_phys (ent),sizeof (*ent), +DMA_TO_DEVICE ); }static struct iommu_domain *exynos_iommu_domain_alloc (unsigned type) {struct exynos_iommu_domain *domain; +dma_addr_t handle;int i;
@@ -694,7 +696,10 @@ static struct iommu_domain *exynos_iommu_domain_alloc(unsigned type) domain->pgtable[i +7 ] =ZERO_LV2LINK ; } -pgtable_flush (domain->pgtable, domain->pgtable +NUM_LV1ENTRIES ); + handle =dma_map_single (dma_dev, domain->pgtable,LV1TABLE_SIZE , +DMA_TO_DEVICE ); +/* For mapping page table entries we rely on dma == phys */ +BUG_ON (handle !=virt_to_phys (domain->pgtable));spin_lock_init (&domain->lock);spin_lock_init (&domain->pgtablelock);
@@ -738,10 +743,18 @@ static void exynos_iommu_domain_free(struct iommu_domain *iommu_domain)if (iommu_domain->type ==IOMMU_DOMAIN_DMA )iommu_put_dma_cookie (iommu_domain); +dma_unmap_single (dma_dev,virt_to_phys (domain->pgtable),LV1TABLE_SIZE , +DMA_TO_DEVICE ); +for (i =0 ; i <NUM_LV1ENTRIES ; i++) -if (lv1ent_page (domain->pgtable + i)) +if (lv1ent_page (domain->pgtable + i)) { +phys_addr_t base =lv2table_base (domain->pgtable + i); + +dma_unmap_single (dma_dev, base,LV2TABLE_SIZE , +DMA_TO_DEVICE );kmem_cache_free (lv2table_kmem_cache, -phys_to_virt (lv2table_base (domain->pgtable + i))); +phys_to_virt (base)); + }free_pages ((unsigned long )domain->pgtable,2 );free_pages ((unsigned long )domain->lv2entcnt,1 );
@@ -834,11 +847,10 @@ static sysmmu_pte_t *alloc_lv2entry(struct exynos_iommu_domain *domain,if (!pent)return ERR_PTR (-ENOMEM ); - *sent =mk_lv1ent_page (virt_to_phys (pent)); +update_pte (sent,mk_lv1ent_page (virt_to_phys (pent)));kmemleak_ignore (pent); *pgcounter =NUM_LV2ENTRIES ; -pgtable_flush (pent, pent +NUM_LV2ENTRIES ); -pgtable_flush (sent, sent +1 ); +dma_map_single (dma_dev, pent,LV2TABLE_SIZE ,DMA_TO_DEVICE );/* * If pre-fetched SLPD is a faulty SLPD in zero_l2_table,
@@ -891,9 +903,7 @@ static int lv1set_section(struct exynos_iommu_domain *domain, *pgcnt =0 ; } - *sent =mk_lv1ent_sect (paddr); - -pgtable_flush (sent, sent +1 ); +update_pte (sent,mk_lv1ent_sect (paddr));spin_lock (&domain->lock);if (lv1ent_page_zero (sent)) {
@@ -917,12 +927,15 @@ static int lv2set_page(sysmmu_pte_t *pent, phys_addr_t paddr, size_t size,if (WARN_ON (!lv2ent_fault (pent)))return -EADDRINUSE ; - *pent =mk_lv2ent_spage (paddr); -pgtable_flush (pent, pent +1 ); +update_pte (pent,mk_lv2ent_spage (paddr)); *pgcnt -=1 ; }else {/* size == LPAGE_SIZE */ int i; +dma_addr_t pent_base =virt_to_phys (pent); +dma_sync_single_for_cpu (dma_dev, pent_base, +sizeof (*pent) *SPAGES_PER_LPAGE , +DMA_TO_DEVICE );for (i =0 ; i <SPAGES_PER_LPAGE ; i++, pent++) {if (WARN_ON (!lv2ent_fault (pent))) {if (i >0 )
@@ -932,7 +945,9 @@ static int lv2set_page(sysmmu_pte_t *pent, phys_addr_t paddr, size_t size, *pent =mk_lv2ent_lpage (paddr); } -pgtable_flush (pent -SPAGES_PER_LPAGE , pent); +dma_sync_single_for_device (dma_dev, pent_base, +sizeof (*pent) *SPAGES_PER_LPAGE , +DMA_TO_DEVICE ); *pgcnt -=SPAGES_PER_LPAGE ; }
@@ -1042,8 +1057,7 @@ static size_t exynos_iommu_unmap(struct iommu_domain *iommu_domain, }/* workaround for h/w bug in System MMU v3.3 */ - *ent =ZERO_LV2LINK ; -pgtable_flush (ent, ent +1 ); +update_pte (ent,ZERO_LV2LINK ); size =SECT_SIZE ; goto done; }
@@ -1064,9 +1078,8 @@ static size_t exynos_iommu_unmap(struct iommu_domain *iommu_domain, }if (lv2ent_small (ent)) { - *ent =0 ; +update_pte (ent,0 ); size =SPAGE_SIZE ; -pgtable_flush (ent, ent +1 ); domain->lv2entcnt[lv1ent_offset (iova)] +=1 ; goto done; }
@@ -1077,9 +1090,13 @@ static size_t exynos_iommu_unmap(struct iommu_domain *iommu_domain, goto err; } +dma_sync_single_for_cpu (dma_dev,virt_to_phys (ent), +sizeof (*ent) *SPAGES_PER_LPAGE , +DMA_TO_DEVICE );memset (ent,0 ,sizeof (*ent) *SPAGES_PER_LPAGE ); -pgtable_flush (ent, ent +SPAGES_PER_LPAGE ); - +dma_sync_single_for_device (dma_dev,virt_to_phys (ent), +sizeof (*ent) *SPAGES_PER_LPAGE , +DMA_TO_DEVICE ); size =LPAGE_SIZE ; domain->lv2entcnt[lv1ent_offset (iova)] +=SPAGES_PER_LPAGE ; done:
@@ -1261,6 +1278,13 @@ static int __init exynos_iommu_of_setup(struct device_node *np)if (IS_ERR (pdev))return PTR_ERR (pdev); +/* +* use the first registered sysmmu device for performing +* dma mapping operations on iommu page tables (cpu cache flush) +*/ +if (!dma_dev) + dma_dev = &pdev->dev; +of_iommu_set_ops (np, &exynos_iommu_ops);return 0 ; }
Signature · 1 line
1.9.2
Marek Szyprowski
maintainerPATCH 4/11 iommu: exynos: simplify master clock operations
In reply to Marek SzyprowskiAll clock API function can be called on NULL clock, so simplify code avoid checking of master clock presence.
--- drivers/iommu/exynos-iommu.c | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-)
drivers/iommu/exynos-iommu.c +12 -20
@@ -333,8 +333,7 @@ static irqreturn_t exynos_sysmmu_irq(int irq, void *dev_id)spin_lock (&data->lock); -if (!IS_ERR (data->clk_master)) -clk_enable (data->clk_master); +clk_enable (data->clk_master); itype = (enum exynos_sysmmu_inttype )__ffs (__raw_readl (data->sfrbase +REG_INT_STATUS ));
@@ -366,8 +365,7 @@ static irqreturn_t exynos_sysmmu_irq(int irq, void *dev_id)sysmmu_unblock (data->sfrbase); -if (!IS_ERR (data->clk_master)) -clk_disable (data->clk_master); +clk_disable (data->clk_master);spin_unlock (&data->lock);
@@ -376,15 +374,13 @@ static irqreturn_t exynos_sysmmu_irq(int irq, void *dev_id)static void __sysmmu_disable_nocount (struct sysmmu_drvdata *data) { -if (!IS_ERR (data->clk_master)) -clk_enable (data->clk_master); +clk_enable (data->clk_master);__raw_writel (CTRL_DISABLE , data->sfrbase +REG_MMU_CTRL );__raw_writel (0 , data->sfrbase +REG_MMU_CFG );clk_disable (data->clk); -if (!IS_ERR (data->clk_master)) -clk_disable (data->clk_master); +clk_disable (data->clk_master); }static bool __sysmmu_disable (struct sysmmu_drvdata *data)
@@ -437,8 +433,7 @@ static void __sysmmu_init_config(struct sysmmu_drvdata *data)static void __sysmmu_enable_nocount (struct sysmmu_drvdata *data) { -if (!IS_ERR (data->clk_master)) -clk_enable (data->clk_master); +clk_enable (data->clk_master);clk_enable (data->clk);__raw_writel (CTRL_BLOCK , data->sfrbase +REG_MMU_CTRL );
@@ -449,8 +444,7 @@ static void __sysmmu_enable_nocount(struct sysmmu_drvdata *data)__raw_writel (CTRL_ENABLE , data->sfrbase +REG_MMU_CTRL ); -if (!IS_ERR (data->clk_master)) -clk_disable (data->clk_master); +clk_disable (data->clk_master); } static int __sysmmu_enable(struct sysmmu_drvdata *data, phys_addr_t pgtable,
@@ -493,16 +487,14 @@ static void sysmmu_tlb_invalidate_flpdcache(struct sysmmu_drvdata *data, {unsigned long flags; -if (!IS_ERR (data->clk_master)) -clk_enable (data->clk_master); +clk_enable (data->clk_master);spin_lock_irqsave (&data->lock, flags);if (is_sysmmu_active (data))__sysmmu_tlb_invalidate_flpdcache (data, iova);spin_unlock_irqrestore (&data->lock, flags); -if (!IS_ERR (data->clk_master)) -clk_disable (data->clk_master); +clk_disable (data->clk_master); } static void sysmmu_tlb_invalidate_entry(struct sysmmu_drvdata *data,
@@ -514,8 +506,7 @@ static void sysmmu_tlb_invalidate_entry(struct sysmmu_drvdata *data,if (is_sysmmu_active (data)) {unsigned int num_inv =1 ; -if (!IS_ERR (data->clk_master)) -clk_enable (data->clk_master); +clk_enable (data->clk_master);/* * L2TLB invalidation required
@@ -535,8 +526,7 @@ static void sysmmu_tlb_invalidate_entry(struct sysmmu_drvdata *data, data->sfrbase, iova, num_inv);sysmmu_unblock (data->sfrbase); } -if (!IS_ERR (data->clk_master)) -clk_disable (data->clk_master); +clk_disable (data->clk_master); }else {dev_dbg (data->master,"disabled. Skipping TLB invalidation @ %#x\n" , iova);
@@ -593,6 +583,8 @@ static int __init exynos_sysmmu_probe(struct platform_device *pdev)dev_err (dev,"Failed to prepare master's clk\n" );return ret; } + }else { + data->clk_master =NULL ; } data->sysmmu = dev;
Signature · 1 line
1.9.2
Marek Szyprowski
maintainerPATCH 5/11 iommu: exynos: refactor code (no direct register access)
In reply to Marek SzyprowskiThis patch changes some internal functions to have access to the state of sysmmu device instead of having only it's registers. This will make the code ready for future extensions.
--- drivers/iommu/exynos-iommu.c | 40 +++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 21 deletions(-)
drivers/iommu/exynos-iommu.c +19 -21
@@ -254,50 +254,49 @@ static bool is_sysmmu_active(struct sysmmu_drvdata *data)return data->activations >0 ; } -static void sysmmu_unblock (void __iomem *sfrbase) +static void sysmmu_unblock (struct sysmmu_drvdata *data) { -__raw_writel (CTRL_ENABLE , sfrbase +REG_MMU_CTRL ); +__raw_writel (CTRL_ENABLE , data->sfrbase +REG_MMU_CTRL ); } -static bool sysmmu_block (void __iomem *sfrbase) +static bool sysmmu_block (struct sysmmu_drvdata *data) {int i =120 ; -__raw_writel (CTRL_BLOCK , sfrbase +REG_MMU_CTRL ); -while ((i >0 ) && !(__raw_readl (sfrbase +REG_MMU_STATUS ) &1 )) +__raw_writel (CTRL_BLOCK , data->sfrbase +REG_MMU_CTRL ); +while ((i >0 ) && !(__raw_readl (data->sfrbase +REG_MMU_STATUS ) &1 )) --i; -if (!(__raw_readl (sfrbase +REG_MMU_STATUS ) &1 )) { -sysmmu_unblock (sfrbase); +if (!(__raw_readl (data->sfrbase +REG_MMU_STATUS ) &1 )) { +sysmmu_unblock (data);return false; }return true; } -static void __sysmmu_tlb_invalidate (void __iomem *sfrbase) +static void __sysmmu_tlb_invalidate (struct sysmmu_drvdata *data) { -__raw_writel (0x1 , sfrbase +REG_MMU_FLUSH ); +__raw_writel (0x1 , data->sfrbase +REG_MMU_FLUSH ); } -static void __sysmmu_tlb_invalidate_entry (void __iomem *sfrbase, +static void __sysmmu_tlb_invalidate_entry (struct sysmmu_drvdata *data,sysmmu_iova_t iova,unsigned int num_inv) {unsigned int i;for (i =0 ; i < num_inv; i++) {__raw_writel ((iova &SPAGE_MASK ) |1 , - sfrbase +REG_MMU_FLUSH_ENTRY ); + data->sfrbase +REG_MMU_FLUSH_ENTRY ); iova +=SPAGE_SIZE ; } } -static void __sysmmu_set_ptbase (void __iomem *sfrbase, -phys_addr_t pgd) +static void __sysmmu_set_ptbase (struct sysmmu_drvdata *data,phys_addr_t pgd) { -__raw_writel (pgd, sfrbase +REG_PT_BASE_ADDR ); +__raw_writel (pgd, data->sfrbase +REG_PT_BASE_ADDR ); -__sysmmu_tlb_invalidate (sfrbase); +__sysmmu_tlb_invalidate (data); } static void show_fault_information(const char *name,
@@ -363,7 +362,7 @@ static irqreturn_t exynos_sysmmu_irq(int irq, void *dev_id)__raw_writel (1 << itype, data->sfrbase +REG_INT_CLEAR ); -sysmmu_unblock (data->sfrbase); +sysmmu_unblock (data);clk_disable (data->clk_master);
@@ -440,7 +439,7 @@ static void __sysmmu_enable_nocount(struct sysmmu_drvdata *data)__sysmmu_init_config (data); -__sysmmu_set_ptbase (data->sfrbase, data->pgtable); +__sysmmu_set_ptbase (data, data->pgtable);__raw_writel (CTRL_ENABLE , data->sfrbase +REG_MMU_CTRL );
@@ -521,10 +520,9 @@ static void sysmmu_tlb_invalidate_entry(struct sysmmu_drvdata *data,if (MMU_MAJ_VER (data->version) ==2 ) num_inv = min_t(unsigned int, size / PAGE_SIZE, 64); -if (sysmmu_block (data->sfrbase)) { -__sysmmu_tlb_invalidate_entry ( - data->sfrbase, iova, num_inv); -sysmmu_unblock (data->sfrbase); +if (sysmmu_block (data)) { +__sysmmu_tlb_invalidate_entry (data, iova, num_inv); +sysmmu_unblock (data); }clk_disable (data->clk_master); }else {
Signature · 1 line
1.9.2
Marek Szyprowski
maintainerPATCH 6/11 iommu: exynos: refactor fault handling code
In reply to Marek SzyprowskiThis patch provides a new implementation for page fault handing code. The new implementation is ready for future extensions. No functional changes have been made.
--- drivers/iommu/exynos-iommu.c | 109 ++++++++++++++++--------------------------- 1 file changed, 41 insertions(+), 68 deletions(-)
drivers/iommu/exynos-iommu.c +41 -68
@@ -148,40 +148,25 @@ static sysmmu_pte_t *page_entry(sysmmu_pte_t *sent, sysmmu_iova_t iova) lv2table_base(sent)) + lv2ent_offset(iova); } -enum exynos_sysmmu_inttype { -SYSMMU_PAGEFAULT , -SYSMMU_AR_MULTIHIT , -SYSMMU_AW_MULTIHIT , -SYSMMU_BUSERROR , -SYSMMU_AR_SECURITY , -SYSMMU_AR_ACCESS , -SYSMMU_AW_SECURITY , -SYSMMU_AW_PROTECTION ,/* 7 */ -SYSMMU_FAULT_UNKNOWN , -SYSMMU_FAULTS_NUM -}; - -static unsigned short fault_reg_offset[SYSMMU_FAULTS_NUM ] = { -REG_PAGE_FAULT_ADDR , -REG_AR_FAULT_ADDR , -REG_AW_FAULT_ADDR , -REG_DEFAULT_SLAVE_ADDR , -REG_AR_FAULT_ADDR , -REG_AR_FAULT_ADDR , -REG_AW_FAULT_ADDR , -REG_AW_FAULT_ADDR +/* +* IOMMU fault information register +*/ +struct sysmmu_fault_info { +unsigned int bit;/* bit number in STATUS register */ +unsigned short addr_reg;/* register to read VA fault address */ +const char *name;/* human readable fault name */ +unsigned int type;/* fault type for report_iommu_fault */ }; -static char *sysmmu_fault_name[SYSMMU_FAULTS_NUM ] = { -"PAGE FAULT" , -"AR MULTI-HIT FAULT" , -"AW MULTI-HIT FAULT" , -"BUS ERROR" , -"AR SECURITY PROTECTION FAULT" , -"AR ACCESS PROTECTION FAULT" , -"AW SECURITY PROTECTION FAULT" , -"AW ACCESS PROTECTION FAULT" , -"UNKNOWN FAULT" +static const struct sysmmu_fault_info sysmmu_faults[] = { + {0 ,REG_PAGE_FAULT_ADDR ,"PAGE" ,IOMMU_FAULT_READ }, + {1 ,REG_AR_FAULT_ADDR ,"AR MULTI-HIT" ,IOMMU_FAULT_READ }, + {2 ,REG_AW_FAULT_ADDR ,"AW MULTI-HIT" ,IOMMU_FAULT_WRITE }, + {3 ,REG_DEFAULT_SLAVE_ADDR ,"BUS ERROR" ,IOMMU_FAULT_READ }, + {4 ,REG_AR_FAULT_ADDR ,"AR SECURITY PROTECTION" ,IOMMU_FAULT_READ }, + {5 ,REG_AR_FAULT_ADDR ,"AR ACCESS PROTECTION" ,IOMMU_FAULT_READ }, + {6 ,REG_AW_FAULT_ADDR ,"AW SECURITY PROTECTION" ,IOMMU_FAULT_WRITE }, + {7 ,REG_AW_FAULT_ADDR ,"AW ACCESS PROTECTION" ,IOMMU_FAULT_WRITE }, };/*
@@ -299,24 +284,19 @@ static void __sysmmu_set_ptbase(struct sysmmu_drvdata *data, phys_addr_t pgd)__sysmmu_tlb_invalidate (data); } -static void show_fault_information (const char *name, -enum exynos_sysmmu_inttype itype, -phys_addr_t pgtable_base,sysmmu_iova_t fault_addr) +static void show_fault_information (struct sysmmu_drvdata *data, +const struct sysmmu_fault_info *finfo, +sysmmu_iova_t fault_addr) {sysmmu_pte_t *ent; -if ((itype >=SYSMMU_FAULTS_NUM ) || (itype <SYSMMU_PAGEFAULT )) - itype =SYSMMU_FAULT_UNKNOWN ; - -pr_err ("%s occurred@%#x by %s(Page table base: %pa)\n" , - sysmmu_fault_name[itype], fault_addr, name, &pgtable_base); - - ent =section_entry (phys_to_virt (pgtable_base), fault_addr); -pr_err ("\tLv1 entry: %#x\n" , *ent); - +dev_err (data->sysmmu,"%s FAULT occurred at %#x (page table base: %pa)\n" , + finfo->name, fault_addr, &data->pgtable); + ent =section_entry (phys_to_virt (data->pgtable), fault_addr); +dev_err (data->sysmmu,"\tLv1 entry: %#x\n" , *ent);if (lv1ent_page (ent)) { ent =page_entry (ent, fault_addr); -pr_err ("\t Lv2 entry: %#x\n" , *ent); +dev_err (data->sysmmu,"\t Lv2 entry: %#x\n" , *ent); } }
@@ -324,8 +304,10 @@ static irqreturn_t exynos_sysmmu_irq(int irq, void *dev_id) {/* SYSMMU is in blocked state when interrupt occurred. */ struct sysmmu_drvdata *data = dev_id; -enum exynos_sysmmu_inttype itype; -sysmmu_iova_t addr =-1 ; +const struct sysmmu_fault_info *finfo = sysmmu_faults; +int i, n =ARRAY_SIZE (sysmmu_faults); +unsigned int itype; +sysmmu_iova_t fault_addr =-1 ;int ret = -ENOSYS ;WARN_ON (!is_sysmmu_active (data));
@@ -334,29 +316,20 @@ static irqreturn_t exynos_sysmmu_irq(int irq, void *dev_id)clk_enable (data->clk_master); - itype = (enum exynos_sysmmu_inttype ) -__ffs (__raw_readl (data->sfrbase +REG_INT_STATUS )); -if (WARN_ON (!((itype >=0 ) && (itype <SYSMMU_FAULT_UNKNOWN )))) - itype =SYSMMU_FAULT_UNKNOWN ; -else - addr =__raw_readl (data->sfrbase + fault_reg_offset[itype]); + itype =__ffs (__raw_readl (data->sfrbase +REG_INT_STATUS )); +for (i =0 ; i < n; i++, finfo++) +if (finfo->bit == itype) +break ; +/* unknown/unsupported fault */ +BUG_ON (i == n); -if (itype ==SYSMMU_FAULT_UNKNOWN ) { -pr_err ("%s: Fault is not occurred by System MMU '%s'!\n" , - __func__,dev_name (data->sysmmu)); -pr_err ("%s: Please check if IRQ is correctly configured.\n" , - __func__); -BUG (); - }else { -unsigned int base = -__raw_readl (data->sfrbase +REG_PT_BASE_ADDR ); -show_fault_information (dev_name (data->sysmmu), - itype, base, addr); -if (data->domain) - ret =report_iommu_fault (&data->domain->domain, - data->master, addr, itype); - } +/* print debug message */ + fault_addr =__raw_readl (data->sfrbase + finfo->addr_reg); +show_fault_information (data, finfo, fault_addr); +if (data->domain) + ret =report_iommu_fault (&data->domain->domain, + data->master, fault_addr, finfo->type);/* fault is not recovered by fault handler */ BUG_ON (ret !=0 );
Signature · 1 line
1.9.2
Marek Szyprowski
maintainerPATCH 7/11 iommu: exynos: refactor init config code
In reply to Marek SzyprowskiThis patch rewrites sysmmu_init_config function to make it easier to read and understand.
--- drivers/iommu/exynos-iommu.c | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-)
drivers/iommu/exynos-iommu.c +9 -16
@@ -383,24 +383,17 @@ static bool __sysmmu_disable(struct sysmmu_drvdata *data)static void __sysmmu_init_config (struct sysmmu_drvdata *data) { -unsigned int cfg =CFG_LRU |CFG_QOS (15 ); -unsigned int ver; - - ver =MMU_RAW_VER (__raw_readl (data->sfrbase +REG_MMU_VERSION )); -if (MMU_MAJ_VER (ver) ==3 ) { -if (MMU_MIN_VER (ver) >=2 ) { - cfg |=CFG_FLPDCACHE ; -if (MMU_MIN_VER (ver) ==3 ) { - cfg |=CFG_ACGEN ; - cfg &= ~CFG_LRU ; - }else { - cfg |=CFG_SYSSEL ; - } - } - } +unsigned int cfg; + + data->version =MMU_RAW_VER (__raw_readl (data->sfrbase +REG_MMU_VERSION )); +if (data->version <=MAKE_MMU_VER (3 ,1 )) + cfg =CFG_LRU |CFG_QOS (15 ); +else if (data->version <=MAKE_MMU_VER (3 ,2 )) + cfg =CFG_LRU |CFG_QOS (15 ) |CFG_FLPDCACHE |CFG_SYSSEL ; +else + cfg =CFG_QOS (15 ) |CFG_FLPDCACHE |CFG_ACGEN ;__raw_writel (cfg, data->sfrbase +REG_MMU_CFG ); - data->version = ver; }static void __sysmmu_enable_nocount (struct sysmmu_drvdata *data)
Signature · 1 line
1.9.2
Marek Szyprowski
maintainerPATCH 8/11 iommu: exynos: unify code for fldp cache invalidation
In reply to Marek SzyprowskiThis patch simplifies the code for handling of flpdcache invalidation.
--- drivers/iommu/exynos-iommu.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-)
drivers/iommu/exynos-iommu.c +4 -9
@@ -440,13 +440,6 @@ static int __sysmmu_enable(struct sysmmu_drvdata *data, phys_addr_t pgtable,return ret; } -static void __sysmmu_tlb_invalidate_flpdcache (struct sysmmu_drvdata *data, -sysmmu_iova_t iova) -{ -if (data->version ==MAKE_MMU_VER (3 ,3 )) -__raw_writel (iova |0x1 , data->sfrbase +REG_MMU_FLUSH_ENTRY ); -} -static void sysmmu_tlb_invalidate_flpdcache (struct sysmmu_drvdata *data,sysmmu_iova_t iova) {
@@ -455,8 +448,10 @@ static void sysmmu_tlb_invalidate_flpdcache(struct sysmmu_drvdata *data,clk_enable (data->clk_master);spin_lock_irqsave (&data->lock, flags); -if (is_sysmmu_active (data)) -__sysmmu_tlb_invalidate_flpdcache (data, iova); +if (is_sysmmu_active (data)) { +if (data->version >=MAKE_MMU_VER (3 ,3 )) +__sysmmu_tlb_invalidate_entry (data, iova,1 ); + }spin_unlock_irqrestore (&data->lock, flags);clk_disable (data->clk_master);
Signature · 1 line
1.9.2
Marek Szyprowski
maintainerPATCH 9/11 iommu: exynos: update device tree documentation
In reply to Marek SzyprowskiExynos SYSMMU bindings documentation was merged before generic IOMMU binding have been introduced. This patch updates documentation to match current state.
--- .../devicetree/bindings/iommu/samsung,sysmmu.txt | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-)
Documentation/devicetree/bindings/iommu/samsung,sysmmu.txt +8 -11
@@ -23,28 +23,23 @@ MMUs. for window 1, 2 and 3. * M2M Scalers and G2D in Exynos5420 has one System MMU on the read channel and the other System MMU on the write channel. -The drivers must consider how to handle those System MMUs. One of the idea is -to implement child devices or sub-devices which are the client devices of the -System MMU. -Note: -The current DT binding for the Exynos System MMU is incomplete. -The following properties can be removed or changed, if found incompatible with -the "Generic IOMMU Binding" support for attaching devices to the IOMMU. +For information on assigning System MMU controller to its peripheral devices, +see generic IOMMU bindings. Required properties: - compatible: Should be "samsung,exynos-sysmmu" - reg: A tuple of base address and size of System MMU registers. +- #iommu-cells: Should be <0>. - interrupt-parent: The phandle of the interrupt controller of System MMU - interrupts: An interrupt specifier for interrupt signal of System MMU, according to the format defined by a particular interrupt controller. - clock-names: Should be "sysmmu" if the System MMU is needed to gate its clock. Optional "master" if the clock to the System MMU is gated by - another gate clock other than "sysmmu". - Exynos4 SoCs, there needs no "master" clock. - Exynos5 SoCs, some System MMUs must have "master" clocks. -- clocks: Required if the System MMU is needed to gate its clock. + another gate clock other than "sysmmu" (usually main gate clock + of peripheral device this SYSMMU belongs to). +- clocks: Phandles for respective clocks described by clock-names. - power-domains: Required if the System MMU is needed to gate its power. Please refer to the following document: Documentation/devicetree/bindings/power/pd-samsung.txt
@@ -57,6 +52,7 @@ Examples: power-domains = <&pd_gsc>; clocks = <&clock CLK_GSCL0>; clock-names = "gscl"; + iommus = <&sysmmu_gsc0>; }; sysmmu_gsc0: sysmmu at 13E80000 {
@@ -67,4 +63,5 @@ Examples: clock-names = "sysmmu", "master"; clocks = <&clock CLK_SMMU_GSCL0>, <&clock CLK_GSCL0>; power-domains = <&pd_gsc>; + #iommu-cells = <0>; };
Signature · 1 line
1.9.2
Marek Szyprowski
maintainerPATCH 10/11 iommu: exynos: add support for v5 SYSMMU
In reply to Marek SzyprowskiThis patch adds support for v5 of SYSMMU controller, found in Samsung Exynos 5433 SoCs. The main difference of v5 is support for 36-bit physical address space and some changes in register layout and core clocks hanging. This patch also adds support for ARM64 architecture, which is used by Exynos 5433 SoCs.
--- .../devicetree/bindings/iommu/samsung,sysmmu.txt | 5 +- drivers/iommu/Kconfig | 2 +- drivers/iommu/exynos-iommu.c | 195 +++++++++++++++------ 3 files changed, 150 insertions(+), 52 deletions(-)
Documentation/devicetree/bindings/iommu/samsung,sysmmu.txt +3 -2
@@ -35,9 +35,10 @@ Required properties: - interrupts: An interrupt specifier for interrupt signal of System MMU, according to the format defined by a particular interrupt controller. -- clock-names: Should be "sysmmu" if the System MMU is needed to gate its clock. +- clock-names: Should be "sysmmu" or a pair of "aclk" and "pclk" to gate + SYSMMU core clocks. Optional "master" if the clock to the System MMU is gated by - another gate clock other than "sysmmu" (usually main gate clock + another gate clock other core (usually main gate clock of peripheral device this SYSMMU belongs to). - clocks: Phandles for respective clocks described by clock-names. - power-domains: Required if the System MMU is needed to gate its power.
drivers/iommu/Kconfig +1 -1
@@ -243,7 +243,7 @@ config TEGRA_IOMMU_SMMU config EXYNOS_IOMMU bool "Exynos IOMMU Support" - depends on ARCH_EXYNOS && ARM && MMU + depends on ARCH_EXYNOS && MMU select IOMMU_API select ARM_DMA_USE_IOMMU help
drivers/iommu/exynos-iommu.c +146 -49
334 lines not on this page · raw message
Signature · 1 line
1.9.2
Marek Szyprowski
maintainerPATCH 11/11 iommu: exynos: add Maintainers entry for Exynos SYSMMU driver
In reply to Marek SzyprowskiAdd Marek Szyprowski as maintainer for Exynos IOMMU driver.
--- MAINTAINERS | 6 ++++++ 1 file changed, 6 insertions(+)
MAINTAINERS +6 -0
@@ -4336,6 +4336,12 @@ L: dri-devel at lists.freedesktop.org S: Maintained F: drivers/gpu/drm/exynos/exynos_dp* +EXYNOS SYSMMU (IOMMU) driver +M: Marek Szyprowski <[email protected]> +L: iommu at lists.linux-foundation.org +S: Maintained +F: drivers/iommu/exynos-iommu.c + EXYNOS MIPI DISPLAY DRIVERS M: Inki Dae <[email protected]> M: Donghwa Lee <[email protected]>
Signature · 1 line
1.9.2
PATCH 11/11 iommu: exynos: add Maintainers entry for Exynos SYSMMU driver
Quoted 7 lines · Marek Szyprowski
Add Marek Szyprowski as maintainer for Exynos IOMMU driver.
Signed-off-by Marek Szyprowski <[email protected]>--- MAINTAINERS | 6 ++++++ 1 file changed, 6 insertions(+)
Best regards,
Krzysztof
Quoted 18 lines
MAINTAINERS +6
-0@@ -4336,6 +4336,12 @@ L: dri-devel at lists.freedesktop.org S: Maintained F: drivers/gpu/drm/exynos/exynos_dp* +EXYNOS SYSMMU (IOMMU) driver +M: Marek Szyprowski <[email protected]> +L: iommu at lists.linux-foundation.org +S: Maintained +F: drivers/iommu/exynos-iommu.c + EXYNOS MIPI DISPLAY DRIVERS M: Inki Dae <[email protected]> M: Donghwa Lee <[email protected]>