From mboxrd@z Thu Jan 1 00:00:00 1970 From: Andrii Tseglytskyi Subject: [PATCH v03 05/10] arm: omap: introduce iommu translation for IPU remoteproc Date: Tue, 2 Sep 2014 18:46:05 +0300 Message-ID: <1409672770-23164-6-git-send-email-andrii.tseglytskyi@globallogic.com> References: <1409672770-23164-1-git-send-email-andrii.tseglytskyi@globallogic.com> Mime-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Return-path: In-Reply-To: <1409672770-23164-1-git-send-email-andrii.tseglytskyi@globallogic.com> List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Sender: xen-devel-bounces@lists.xen.org Errors-To: xen-devel-bounces@lists.xen.org To: Ian Campbell , Stefano Stabellini , Julien Grall , xen-devel@lists.xen.org List-Id: xen-devel@lists.xenproject.org The following patch introduced platform specific MMU data definitions and pagetable translation function for OMAP5 IPU remoteproc. This MMU is a bit specific - it typically performs one level translation and map a big chunks of memory. 16 Mb supersections and 1 Mb sections are mapped instead of 4 Kb pages. Introduced algorithm performs internal remapping of big sections to small 4 Kb pages. Signed-off-by: Andrii Tseglytskyi --- xen/arch/arm/remoteproc/Makefile | 1 + xen/arch/arm/remoteproc/omap_iommu.c | 325 +++++++++++++++++++++++++++++ xen/arch/arm/remoteproc/remoteproc_iommu.c | 1 + xen/include/asm-arm/remoteproc_iommu.h | 2 + 4 files changed, 329 insertions(+) create mode 100644 xen/arch/arm/remoteproc/omap_iommu.c diff --git a/xen/arch/arm/remoteproc/Makefile b/xen/arch/arm/remoteproc/Makefile index 0b0ee0e..0564c1a 100644 --- a/xen/arch/arm/remoteproc/Makefile +++ b/xen/arch/arm/remoteproc/Makefile @@ -1 +1,2 @@ obj-y += remoteproc_iommu.o +obj-y += omap_iommu.o diff --git a/xen/arch/arm/remoteproc/omap_iommu.c b/xen/arch/arm/remoteproc/omap_iommu.c new file mode 100644 index 0000000..8ed6d0b --- /dev/null +++ b/xen/arch/arm/remoteproc/omap_iommu.c @@ -0,0 +1,325 @@ +/* + * xen/arch/arm/platforms/omap_iommu.c + * + * Andrii Tseglytskyi + * Copyright (c) 2014 GlobalLogic + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +/* + * "L2 table" address mask and size definitions. + */ + +/* register where address of pagetable is stored */ +#define MMU_IPU_TTB_OFFSET 0x4c + +/* 1st level translation */ +#define MMU_OMAP_PGD_SHIFT 20 +#define MMU_OMAP_SUPER_SHIFT 24 /* "supersection" - 16 Mb */ +#define MMU_OMAP_SECTION_SHIFT 20 /* "section" - 1 Mb */ +#define MMU_OMAP_SECOND_LEVEL_SHIFT 10 + +/* 2nd level translation */ +#define MMU_OMAP_PTE_SMALL_SHIFT 12 /* "small page" - 4Kb */ +#define MMU_OMAP_PTE_LARGE_SHIFT 16 /* "large page" - 64 Kb */ + +/* + * some descriptor attributes. + */ +#define IPU_PGD_TABLE (1 << 0) +#define IPU_PGD_SECTION (2 << 0) +#define IPU_PGD_SUPER (1 << 18 | 2 << 0) + +#define ipu_pgd_is_table(x) (((x) & 3) == IPU_PGD_TABLE) +#define ipu_pgd_is_section(x) (((x) & (1 << 18 | 3)) == IPU_PGD_SECTION) +#define ipu_pgd_is_super(x) (((x) & (1 << 18 | 3)) == IPU_PGD_SUPER) + +#define IPU_PTE_SMALL (2 << 0) +#define IPU_PTE_LARGE (1 << 0) + +#define OMAP_IPU_MMU_MEM_BASE 0x55082000 + +static int mmu_omap_copy_pagetable(struct mmu_info *mmu, struct mmu_pagetable *pgt); + +static paddr_t mmu_ipu_translate_pagetable(struct mmu_info *mmu, struct mmu_pagetable *pgt); + +static u32 ipu_trap_offsets[] = { + MMU_IPU_TTB_OFFSET, +}; + +static const struct pagetable_data pagetable_ipu_data = { + .pgd_shift = MMU_OMAP_PGD_SHIFT, + .super_shift = MMU_OMAP_SUPER_SHIFT, + .section_shift = MMU_OMAP_SECTION_SHIFT, + .pte_shift = MMU_OMAP_PTE_SMALL_SHIFT, + .pte_large_shift = MMU_OMAP_PTE_LARGE_SHIFT, +}; + +struct mmu_info omap_ipu_mmu = { + .name = "IPU_L2_MMU", + .pg_data = &pagetable_ipu_data, + .trap_offsets = ipu_trap_offsets, + .mem_start = OMAP_IPU_MMU_MEM_BASE, + .mem_size = 0x1000, + .num_traps = ARRAY_SIZE(ipu_trap_offsets), + .copy_pagetable_pfunc = mmu_omap_copy_pagetable, + .translate_pfunc = mmu_ipu_translate_pagetable, +}; + +static bool translate_supersections_to_pages = true; +static bool translate_sections_to_pages = true; + +static int mmu_omap_copy_pagetable(struct mmu_info *mmu, struct mmu_pagetable *pgt) +{ + void *pagetable = NULL; + paddr_t maddr; + u32 i; + + ASSERT(mmu); + ASSERT(pgt); + + if ( !pgt->paddr ) + return -EINVAL; + + /* pagetable size can be more than one page */ + for ( i = 0; i < MMU_PGD_TABLE_SIZE(mmu) / PAGE_SIZE; i++ ) + { + /* lookup address where remoteproc pagetable is stored by kernel */ + maddr = p2m_lookup(current->domain, pgt->paddr + i * PAGE_SIZE, NULL); + if ( INVALID_PADDR == maddr ) + { + pr_mmu(mmu, "failed to translate 0x%"PRIpaddr" to maddr", pgt->paddr + i * PAGE_SIZE); + return -EINVAL; + } + + pagetable = map_domain_page(maddr>>PAGE_SHIFT); + if ( !pagetable ) + { + pr_mmu(mmu, "failed to map pagetable"); + return -EINVAL; + } + + /* copy pagetable to hypervisor memory */ + clean_and_invalidate_xen_dcache_va_range(pagetable, PAGE_SIZE); + memcpy((u32*)((u32)pgt->kern_pagetable + i * PAGE_SIZE), pagetable, PAGE_SIZE); + + unmap_domain_page(pagetable); + } + + return 0; +} + +static paddr_t mmu_pte_table_alloc(struct mmu_info *mmu, paddr_t pgd, u32 sect_num, + struct mmu_pagetable *pgt, paddr_t hyp_addr) +{ + u32 *pte = NULL; + u32 i; + + /* allocate pte table once */ + if ( 0 == hyp_addr ) + { + pte = xzalloc_bytes(PAGE_SIZE); + if ( !pte ) + { + pr_mmu(mmu, "failed to alloc 2nd level table"); + return INVALID_PADDR; + } + } + else + { + pte = __va(hyp_addr & MMU_SECTION_MASK(mmu->pg_data->pte_shift)); + } + + ASSERT(256 == MMU_PTRS_PER_PTE(mmu)); + + for ( i = 0; i < MMU_PTRS_PER_PTE(mmu); i++ ) + { + paddr_t paddr, maddr; + int res; + + paddr = pgd + (i * PAGE_SIZE); + maddr = p2m_lookup(current->domain, paddr, NULL); + + if ( INVALID_PADDR == maddr ) + { + pr_mmu(mmu, "failed to lookup paddr 0x%"PRIpaddr"", paddr); + return INVALID_PADDR; + } + + if ( !guest_physmap_pinned_range(current->domain, maddr >> PAGE_SHIFT, 0) ) + { + res = guest_physmap_pin_range(current->domain, maddr >> PAGE_SHIFT, 0); + if ( res ) + { + pr_mmu(mmu, "can't pin page pfn 0x%"PRIpaddr" mfn 0x%"PRIpaddr" res %d", + paddr, maddr, res); + return INVALID_PADDR; + } + } + + pte[i] = maddr | IPU_PTE_SMALL; + pgt->page_counter++; + } + + clean_and_invalidate_xen_dcache_va_range(pte, PAGE_SIZE); + return __pa(pte) | IPU_PGD_TABLE; +} + +static paddr_t mmu_ipu_translate_pagetable(struct mmu_info *mmu, struct mmu_pagetable *pgt) +{ + /* IPU pagetable consists of set of 32 bit pointers */ + u32 *kern_pgt, *hyp_pgt; + const struct pagetable_data *data; + u32 i; + + ASSERT(mmu); + ASSERT(pgt); + + data = mmu->pg_data; + kern_pgt = pgt->kern_pagetable; + hyp_pgt = pgt->hyp_pagetable; + pgt->page_counter = 0; + + ASSERT(4096 == MMU_PTRS_PER_PGD(mmu)); + + /* 1-st level translation */ + for ( i = 0; i < MMU_PTRS_PER_PGD(mmu); i++ ) + { + paddr_t pd_maddr, pd_paddr, pd_flags, pgd_tmp; + paddr_t pgd = kern_pgt[i]; + u32 pd_mask = 0; + int res; + + if ( !pgd ) + { + /* handle the case when second level translation table + * was removed from kernel */ + if ( unlikely(hyp_pgt[i]) ) + { + guest_physmap_unpin_range(current->domain, + (hyp_pgt[i] & MMU_SECTION_MASK(MMU_OMAP_SECOND_LEVEL_SHIFT)) >> PAGE_SHIFT, 0); + xfree(__va(hyp_pgt[i] & MMU_SECTION_MASK(MMU_OMAP_SECOND_LEVEL_SHIFT))); + hyp_pgt[i] = 0; + } + + continue; + } + + /* first level pointers have different formats, depending on their type */ + if ( ipu_pgd_is_super(pgd) ) + pd_mask = MMU_SECTION_MASK(MMU_OMAP_SUPER_SHIFT); + else if ( ipu_pgd_is_section(pgd) ) + pd_mask = MMU_SECTION_MASK(MMU_OMAP_SECTION_SHIFT); + else if ( ipu_pgd_is_table(pgd) ) + pd_mask = MMU_SECTION_MASK(MMU_OMAP_SECOND_LEVEL_SHIFT); + + pd_paddr = pgd & pd_mask; + pd_flags = pgd & ~pd_mask; + pd_maddr = p2m_lookup(current->domain, pd_paddr, NULL); + + if ( INVALID_PADDR == pd_maddr ) + { + pr_mmu(mmu, "failed to lookup paddr 0x%"PRIpaddr"", pd_paddr); + return INVALID_PADDR; + } + + if ( !guest_physmap_pinned_range(current->domain, pd_maddr >> PAGE_SHIFT, 0) ) + { + res = guest_physmap_pin_range(current->domain, pd_maddr >> PAGE_SHIFT, 0); + if ( res ) + { + pr_mmu(mmu, "can't pin page pfn 0x%"PRIpaddr" mfn 0x%"PRIpaddr" res %d", pd_paddr, pd_maddr, res); + return INVALID_PADDR; + } + } + + /* "supersection" 16 Mb */ + if ( ipu_pgd_is_super(pgd) ) + { + /* mapping of 16 Mb chunk is fragmented to 4 Kb pages */ + if( likely(translate_supersections_to_pages) ) + { + u32 j; + + ASSERT(16 == MMU_SECTION_PER_SUPER(mmu)); + ASSERT(1048576 == MMU_SECTION_SIZE(data->section_shift)); + + /* 16 Mb supersection is divided to 16 sections of 1 MB size */ + for ( j = 0 ; j < MMU_SECTION_PER_SUPER(mmu); j++ ) + { + pgd_tmp = (pgd & ~IPU_PGD_SUPER) + (j * MMU_SECTION_SIZE(data->section_shift)); + hyp_pgt[i + j] = mmu_pte_table_alloc(mmu, pgd_tmp, i, pgt, hyp_pgt[i + j]); + } + + /* move counter after supersection is translated */ + i += (j - 1); + } + else + { + hyp_pgt[i] = pd_maddr | pd_flags; + } + + /* "section" 1Mb */ + } + else if ( ipu_pgd_is_section(pgd) ) + { + if ( likely(translate_sections_to_pages) ) + { + pgd_tmp = (pgd & ~IPU_PGD_SECTION); + hyp_pgt[i] = mmu_pte_table_alloc(mmu, pgd_tmp, i, pgt, hyp_pgt[i]); + } + else + { + hyp_pgt[i] = pd_maddr | pd_flags; + } + + /* "table" */ + } + else if ( unlikely(ipu_pgd_is_table(pgd)) ) + { + ASSERT(256 == MMU_PTRS_PER_PTE(mmu)); + + hyp_pgt[i] = remoteproc_iommu_translate_second_level(mmu, pgt, pd_maddr, hyp_pgt[i]); + hyp_pgt[i] |= pd_flags; + + /* error */ + } + else + { + pr_mmu(mmu, "unknown entry %u: 0x%"PRIpaddr"", i, pgd); + return INVALID_PADDR; + } + } + + /* force omap IOMMU to use new pagetable */ + clean_and_invalidate_xen_dcache_va_range(hyp_pgt, MMU_PGD_TABLE_SIZE(mmu)); + return __pa(hyp_pgt); +} + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/arch/arm/remoteproc/remoteproc_iommu.c b/xen/arch/arm/remoteproc/remoteproc_iommu.c index e73711a..a2cae25 100644 --- a/xen/arch/arm/remoteproc/remoteproc_iommu.c +++ b/xen/arch/arm/remoteproc/remoteproc_iommu.c @@ -32,6 +32,7 @@ #include static struct mmu_info *mmu_list[] = { + &omap_ipu_mmu, }; #define mmu_for_each(pfunc, data) \ diff --git a/xen/include/asm-arm/remoteproc_iommu.h b/xen/include/asm-arm/remoteproc_iommu.h index 6fa78ee..e581fc3 100644 --- a/xen/include/asm-arm/remoteproc_iommu.h +++ b/xen/include/asm-arm/remoteproc_iommu.h @@ -79,4 +79,6 @@ paddr_t remoteproc_iommu_translate_second_level(struct mmu_info *mmu, struct mmu_pagetable *pgt, paddr_t maddr, paddr_t hyp_addr); +extern struct mmu_info omap_ipu_mmu; + #endif /* _REMOTEPROC_IOMMU_H_ */ -- 1.9.1