diff options
Diffstat (limited to 'drivers/staging/media/atomisp/pci/atomisp2/mmu/isp_mmu.c')
-rw-r--r-- | drivers/staging/media/atomisp/pci/atomisp2/mmu/isp_mmu.c | 584 |
1 files changed, 0 insertions, 584 deletions
diff --git a/drivers/staging/media/atomisp/pci/atomisp2/mmu/isp_mmu.c b/drivers/staging/media/atomisp/pci/atomisp2/mmu/isp_mmu.c deleted file mode 100644 index 198f29f4a324..000000000000 --- a/drivers/staging/media/atomisp/pci/atomisp2/mmu/isp_mmu.c +++ /dev/null @@ -1,584 +0,0 @@ -/* - * Support for Medifield PNW Camera Imaging ISP subsystem. - * - * Copyright (c) 2010 Intel Corporation. All Rights Reserved. - * - * Copyright (c) 2010 Silicon Hive www.siliconhive.com. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version - * 2 as published by the Free Software Foundation. - * - * 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. - * - * - */ -/* - * ISP MMU management wrap code - */ -#include <linux/kernel.h> -#include <linux/types.h> -#include <linux/gfp.h> -#include <linux/mm.h> /* for GFP_ATOMIC */ -#include <linux/slab.h> /* for kmalloc */ -#include <linux/list.h> -#include <linux/io.h> -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/string.h> -#include <linux/errno.h> -#include <linux/sizes.h> - -#ifdef CONFIG_X86 -#include <asm/set_memory.h> -#endif - -#include "atomisp_internal.h" -#include "mmu/isp_mmu.h" - -/* - * 64-bit x86 processor physical address layout: - * 0 - 0x7fffffff DDR RAM (2GB) - * 0x80000000 - 0xffffffff MMIO (2GB) - * 0x100000000 - 0x3fffffffffff DDR RAM (64TB) - * So if the system has more than 2GB DDR memory, the lower 2GB occupies the - * physical address 0 - 0x7fffffff and the rest will start from 0x100000000. - * We have to make sure memory is allocated from the lower 2GB for devices - * that are only 32-bit capable(e.g. the ISP MMU). - * - * For any confusion, contact bin.gao@intel.com. - */ -#define NR_PAGES_2GB (SZ_2G / PAGE_SIZE) - -static void free_mmu_map(struct isp_mmu *mmu, unsigned int start_isp_virt, - unsigned int end_isp_virt); - -static unsigned int atomisp_get_pte(phys_addr_t pt, unsigned int idx) -{ - unsigned int *pt_virt = phys_to_virt(pt); - return *(pt_virt + idx); -} - -static void atomisp_set_pte(phys_addr_t pt, - unsigned int idx, unsigned int pte) -{ - unsigned int *pt_virt = phys_to_virt(pt); - *(pt_virt + idx) = pte; -} - -static void *isp_pt_phys_to_virt(phys_addr_t phys) -{ - return phys_to_virt(phys); -} - -static phys_addr_t isp_pte_to_pgaddr(struct isp_mmu *mmu, - unsigned int pte) -{ - return mmu->driver->pte_to_phys(mmu, pte); -} - -static unsigned int isp_pgaddr_to_pte_valid(struct isp_mmu *mmu, - phys_addr_t phys) -{ - unsigned int pte = mmu->driver->phys_to_pte(mmu, phys); - return (unsigned int) (pte | ISP_PTE_VALID_MASK(mmu)); -} - -/* - * allocate a uncacheable page table. - * return physical address. - */ -static phys_addr_t alloc_page_table(struct isp_mmu *mmu) -{ - int i; - phys_addr_t page; - void *virt; - - /*page table lock may needed here*/ - /* - * The slab allocator(kmem_cache and kmalloc family) doesn't handle - * GFP_DMA32 flag, so we have to use buddy allocator. - */ - if (totalram_pages > (unsigned long)NR_PAGES_2GB) - virt = (void *)__get_free_page(GFP_KERNEL | GFP_DMA32); - else - virt = kmem_cache_zalloc(mmu->tbl_cache, GFP_KERNEL); - if (!virt) - return (phys_addr_t)NULL_PAGE; - - /* - * we need a uncacheable page table. - */ -#ifdef CONFIG_X86 - set_memory_uc((unsigned long)virt, 1); -#endif - - page = virt_to_phys(virt); - - for (i = 0; i < 1024; i++) { - /* NEED CHECK */ - atomisp_set_pte(page, i, mmu->driver->null_pte); - } - - return page; -} - -static void free_page_table(struct isp_mmu *mmu, phys_addr_t page) -{ - void *virt; - page &= ISP_PAGE_MASK; - /* - * reset the page to write back before free - */ - virt = phys_to_virt(page); - -#ifdef CONFIG_X86 - set_memory_wb((unsigned long)virt, 1); -#endif - - kmem_cache_free(mmu->tbl_cache, virt); -} - -static void mmu_remap_error(struct isp_mmu *mmu, - phys_addr_t l1_pt, unsigned int l1_idx, - phys_addr_t l2_pt, unsigned int l2_idx, - unsigned int isp_virt, phys_addr_t old_phys, - phys_addr_t new_phys) -{ - dev_err(atomisp_dev, "address remap:\n\n" - "\tL1 PT: virt = %p, phys = 0x%llx, " - "idx = %d\n" - "\tL2 PT: virt = %p, phys = 0x%llx, " - "idx = %d\n" - "\told: isp_virt = 0x%x, phys = 0x%llx\n" - "\tnew: isp_virt = 0x%x, phys = 0x%llx\n", - isp_pt_phys_to_virt(l1_pt), - (u64)l1_pt, l1_idx, - isp_pt_phys_to_virt(l2_pt), - (u64)l2_pt, l2_idx, isp_virt, - (u64)old_phys, isp_virt, - (u64)new_phys); -} - -static void mmu_unmap_l2_pte_error(struct isp_mmu *mmu, - phys_addr_t l1_pt, unsigned int l1_idx, - phys_addr_t l2_pt, unsigned int l2_idx, - unsigned int isp_virt, unsigned int pte) -{ - dev_err(atomisp_dev, "unmap unvalid L2 pte:\n\n" - "\tL1 PT: virt = %p, phys = 0x%llx, " - "idx = %d\n" - "\tL2 PT: virt = %p, phys = 0x%llx, " - "idx = %d\n" - "\tisp_virt = 0x%x, pte(page phys) = 0x%x\n", - isp_pt_phys_to_virt(l1_pt), - (u64)l1_pt, l1_idx, - isp_pt_phys_to_virt(l2_pt), - (u64)l2_pt, l2_idx, isp_virt, - pte); -} - -static void mmu_unmap_l1_pte_error(struct isp_mmu *mmu, - phys_addr_t l1_pt, unsigned int l1_idx, - unsigned int isp_virt, unsigned int pte) -{ - dev_err(atomisp_dev, "unmap unvalid L1 pte (L2 PT):\n\n" - "\tL1 PT: virt = %p, phys = 0x%llx, " - "idx = %d\n" - "\tisp_virt = 0x%x, l1_pte(L2 PT) = 0x%x\n", - isp_pt_phys_to_virt(l1_pt), - (u64)l1_pt, l1_idx, (unsigned int)isp_virt, - pte); -} - -static void mmu_unmap_l1_pt_error(struct isp_mmu *mmu, unsigned int pte) -{ - dev_err(atomisp_dev, "unmap unvalid L1PT:\n\n" - "L1PT = 0x%x\n", (unsigned int)pte); -} - -/* - * Update L2 page table according to isp virtual address and page physical - * address - */ -static int mmu_l2_map(struct isp_mmu *mmu, phys_addr_t l1_pt, - unsigned int l1_idx, phys_addr_t l2_pt, - unsigned int start, unsigned int end, phys_addr_t phys) -{ - unsigned int ptr; - unsigned int idx; - unsigned int pte; - - l2_pt &= ISP_PAGE_MASK; - - start = start & ISP_PAGE_MASK; - end = ISP_PAGE_ALIGN(end); - phys &= ISP_PAGE_MASK; - - ptr = start; - do { - idx = ISP_PTR_TO_L2_IDX(ptr); - - pte = atomisp_get_pte(l2_pt, idx); - - if (ISP_PTE_VALID(mmu, pte)) { - mmu_remap_error(mmu, l1_pt, l1_idx, - l2_pt, idx, ptr, pte, phys); - - /* free all mapped pages */ - free_mmu_map(mmu, start, ptr); - - return -EINVAL; - } - - pte = isp_pgaddr_to_pte_valid(mmu, phys); - - atomisp_set_pte(l2_pt, idx, pte); - mmu->l2_pgt_refcount[l1_idx]++; - ptr += (1U << ISP_L2PT_OFFSET); - phys += (1U << ISP_L2PT_OFFSET); - } while (ptr < end && idx < ISP_L2PT_PTES - 1); - - return 0; -} - -/* - * Update L1 page table according to isp virtual address and page physical - * address - */ -static int mmu_l1_map(struct isp_mmu *mmu, phys_addr_t l1_pt, - unsigned int start, unsigned int end, - phys_addr_t phys) -{ - phys_addr_t l2_pt; - unsigned int ptr, l1_aligned; - unsigned int idx; - unsigned int l2_pte; - int ret; - - l1_pt &= ISP_PAGE_MASK; - - start = start & ISP_PAGE_MASK; - end = ISP_PAGE_ALIGN(end); - phys &= ISP_PAGE_MASK; - - ptr = start; - do { - idx = ISP_PTR_TO_L1_IDX(ptr); - - l2_pte = atomisp_get_pte(l1_pt, idx); - - if (!ISP_PTE_VALID(mmu, l2_pte)) { - l2_pt = alloc_page_table(mmu); - if (l2_pt == NULL_PAGE) { - dev_err(atomisp_dev, - "alloc page table fail.\n"); - - /* free all mapped pages */ - free_mmu_map(mmu, start, ptr); - - return -ENOMEM; - } - - l2_pte = isp_pgaddr_to_pte_valid(mmu, l2_pt); - - atomisp_set_pte(l1_pt, idx, l2_pte); - mmu->l2_pgt_refcount[idx] = 0; - } - - l2_pt = isp_pte_to_pgaddr(mmu, l2_pte); - - l1_aligned = (ptr & ISP_PAGE_MASK) + (1U << ISP_L1PT_OFFSET); - - if (l1_aligned < end) { - ret = mmu_l2_map(mmu, l1_pt, idx, - l2_pt, ptr, l1_aligned, phys); - phys += (l1_aligned - ptr); - ptr = l1_aligned; - } else { - ret = mmu_l2_map(mmu, l1_pt, idx, - l2_pt, ptr, end, phys); - phys += (end - ptr); - ptr = end; - } - - if (ret) { - dev_err(atomisp_dev, "setup mapping in L2PT fail.\n"); - - /* free all mapped pages */ - free_mmu_map(mmu, start, ptr); - - return -EINVAL; - } - } while (ptr < end && idx < ISP_L1PT_PTES); - - return 0; -} - -/* - * Update page table according to isp virtual address and page physical - * address - */ -static int mmu_map(struct isp_mmu *mmu, unsigned int isp_virt, - phys_addr_t phys, unsigned int pgnr) -{ - unsigned int start, end; - phys_addr_t l1_pt; - int ret; - - mutex_lock(&mmu->pt_mutex); - if (!ISP_PTE_VALID(mmu, mmu->l1_pte)) { - /* - * allocate 1 new page for L1 page table - */ - l1_pt = alloc_page_table(mmu); - if (l1_pt == NULL_PAGE) { - dev_err(atomisp_dev, "alloc page table fail.\n"); - mutex_unlock(&mmu->pt_mutex); - return -ENOMEM; - } - - /* - * setup L1 page table physical addr to MMU - */ - mmu->base_address = l1_pt; - mmu->l1_pte = isp_pgaddr_to_pte_valid(mmu, l1_pt); - memset(mmu->l2_pgt_refcount, 0, sizeof(int) * ISP_L1PT_PTES); - } - - l1_pt = isp_pte_to_pgaddr(mmu, mmu->l1_pte); - - start = (isp_virt) & ISP_PAGE_MASK; - end = start + (pgnr << ISP_PAGE_OFFSET); - phys &= ISP_PAGE_MASK; - - ret = mmu_l1_map(mmu, l1_pt, start, end, phys); - - if (ret) - dev_err(atomisp_dev, "setup mapping in L1PT fail.\n"); - - mutex_unlock(&mmu->pt_mutex); - return ret; -} - -/* - * Free L2 page table according to isp virtual address and page physical - * address - */ -static void mmu_l2_unmap(struct isp_mmu *mmu, phys_addr_t l1_pt, - unsigned int l1_idx, phys_addr_t l2_pt, - unsigned int start, unsigned int end) -{ - - unsigned int ptr; - unsigned int idx; - unsigned int pte; - - l2_pt &= ISP_PAGE_MASK; - - start = start & ISP_PAGE_MASK; - end = ISP_PAGE_ALIGN(end); - - ptr = start; - do { - idx = ISP_PTR_TO_L2_IDX(ptr); - - pte = atomisp_get_pte(l2_pt, idx); - - if (!ISP_PTE_VALID(mmu, pte)) - mmu_unmap_l2_pte_error(mmu, l1_pt, l1_idx, - l2_pt, idx, ptr, pte); - - atomisp_set_pte(l2_pt, idx, mmu->driver->null_pte); - mmu->l2_pgt_refcount[l1_idx]--; - ptr += (1U << ISP_L2PT_OFFSET); - } while (ptr < end && idx < ISP_L2PT_PTES - 1); - - if (mmu->l2_pgt_refcount[l1_idx] == 0) { - free_page_table(mmu, l2_pt); - atomisp_set_pte(l1_pt, l1_idx, mmu->driver->null_pte); - } -} - -/* - * Free L1 page table according to isp virtual address and page physical - * address - */ -static void mmu_l1_unmap(struct isp_mmu *mmu, phys_addr_t l1_pt, - unsigned int start, unsigned int end) -{ - phys_addr_t l2_pt; - unsigned int ptr, l1_aligned; - unsigned int idx; - unsigned int l2_pte; - - l1_pt &= ISP_PAGE_MASK; - - start = start & ISP_PAGE_MASK; - end = ISP_PAGE_ALIGN(end); - - ptr = start; - do { - idx = ISP_PTR_TO_L1_IDX(ptr); - - l2_pte = atomisp_get_pte(l1_pt, idx); - - if (!ISP_PTE_VALID(mmu, l2_pte)) { - mmu_unmap_l1_pte_error(mmu, l1_pt, idx, ptr, l2_pte); - continue; - } - - l2_pt = isp_pte_to_pgaddr(mmu, l2_pte); - - l1_aligned = (ptr & ISP_PAGE_MASK) + (1U << ISP_L1PT_OFFSET); - - if (l1_aligned < end) { - mmu_l2_unmap(mmu, l1_pt, idx, l2_pt, ptr, l1_aligned); - ptr = l1_aligned; - } else { - mmu_l2_unmap(mmu, l1_pt, idx, l2_pt, ptr, end); - ptr = end; - } - /* - * use the same L2 page next time, so we don't - * need to invalidate and free this PT. - */ - /* atomisp_set_pte(l1_pt, idx, NULL_PTE); */ - } while (ptr < end && idx < ISP_L1PT_PTES); -} - -/* - * Free page table according to isp virtual address and page physical - * address - */ -static void mmu_unmap(struct isp_mmu *mmu, unsigned int isp_virt, - unsigned int pgnr) -{ - unsigned int start, end; - phys_addr_t l1_pt; - - mutex_lock(&mmu->pt_mutex); - if (!ISP_PTE_VALID(mmu, mmu->l1_pte)) { - mmu_unmap_l1_pt_error(mmu, mmu->l1_pte); - mutex_unlock(&mmu->pt_mutex); - return; - } - - l1_pt = isp_pte_to_pgaddr(mmu, mmu->l1_pte); - - start = (isp_virt) & ISP_PAGE_MASK; - end = start + (pgnr << ISP_PAGE_OFFSET); - - mmu_l1_unmap(mmu, l1_pt, start, end); - mutex_unlock(&mmu->pt_mutex); -} - -/* - * Free page tables according to isp start virtual address and end virtual - * address. - */ -static void free_mmu_map(struct isp_mmu *mmu, unsigned int start_isp_virt, - unsigned int end_isp_virt) -{ - unsigned int pgnr; - unsigned int start, end; - - start = (start_isp_virt) & ISP_PAGE_MASK; - end = (end_isp_virt) & ISP_PAGE_MASK; - pgnr = (end - start) >> ISP_PAGE_OFFSET; - mmu_unmap(mmu, start, pgnr); -} - -int isp_mmu_map(struct isp_mmu *mmu, unsigned int isp_virt, - phys_addr_t phys, unsigned int pgnr) -{ - return mmu_map(mmu, isp_virt, phys, pgnr); -} - -void isp_mmu_unmap(struct isp_mmu *mmu, unsigned int isp_virt, - unsigned int pgnr) -{ - mmu_unmap(mmu, isp_virt, pgnr); -} - -static void isp_mmu_flush_tlb_range_default(struct isp_mmu *mmu, - unsigned int start, - unsigned int size) -{ - isp_mmu_flush_tlb(mmu); -} - -/*MMU init for internal structure*/ -int isp_mmu_init(struct isp_mmu *mmu, struct isp_mmu_client *driver) -{ - if (!mmu) /* error */ - return -EINVAL; - if (!driver) /* error */ - return -EINVAL; - - if (!driver->name) - dev_warn(atomisp_dev, "NULL name for MMU driver...\n"); - - mmu->driver = driver; - - if (!driver->tlb_flush_all) { - dev_err(atomisp_dev, "tlb_flush_all operation not provided.\n"); - return -EINVAL; - } - - if (!driver->tlb_flush_range) - driver->tlb_flush_range = isp_mmu_flush_tlb_range_default; - - if (!driver->pte_valid_mask) { - dev_err(atomisp_dev, "PTE_MASK is missing from mmu driver\n"); - return -EINVAL; - } - - mmu->l1_pte = driver->null_pte; - - mutex_init(&mmu->pt_mutex); - - mmu->tbl_cache = kmem_cache_create("iopte_cache", ISP_PAGE_SIZE, - ISP_PAGE_SIZE, SLAB_HWCACHE_ALIGN, - NULL); - if (!mmu->tbl_cache) - return -ENOMEM; - - return 0; -} - -/*Free L1 and L2 page table*/ -void isp_mmu_exit(struct isp_mmu *mmu) -{ - unsigned int idx; - unsigned int pte; - phys_addr_t l1_pt, l2_pt; - - if (!mmu) - return; - - if (!ISP_PTE_VALID(mmu, mmu->l1_pte)) { - dev_warn(atomisp_dev, "invalid L1PT: pte = 0x%x\n", - (unsigned int)mmu->l1_pte); - return; - } - - l1_pt = isp_pte_to_pgaddr(mmu, mmu->l1_pte); - - for (idx = 0; idx < ISP_L1PT_PTES; idx++) { - pte = atomisp_get_pte(l1_pt, idx); - - if (ISP_PTE_VALID(mmu, pte)) { - l2_pt = isp_pte_to_pgaddr(mmu, pte); - - free_page_table(mmu, l2_pt); - } - } - - free_page_table(mmu, l1_pt); - - kmem_cache_destroy(mmu->tbl_cache); -} |