aboutsummaryrefslogtreecommitdiff
path: root/drivers/staging/media/atomisp/pci/atomisp2/mmu/isp_mmu.c
diff options
context:
space:
mode:
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.c584
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);
-}