iommu: iommu code changes

Description:code iommu

Team:HISI_DRV
Feature or Bugfix:Feature

Change-Id: Iab438728b0e08ed2e177156d4e6a9aba3f57fe5a
Signed-off-by: c00261379 <c00261379@notesmail.huawei.com>
Reviewed-on: http://10.141.107.74:6060/47
Pre-Build: chenfeng 00261379 <puck.chen@hisilicon.com>
Reviewed-by: chenfeng 00261379 <puck.chen@hisilicon.com>
Tested-by: public phisik3jenkins <public_phisik3jenkins@notesmail.huawei.com>
Reviewed-by: suzhuangluan 00300654 <suzhuangluan@hisilicon.com>
[jsutltz: split out all the ion changes, and kept just the iommu bits]
Signed-off-by: John Stultz <john.stultz@linaro.org>
This commit is contained in:
c00261379
2016-10-19 10:31:07 +08:00
committed by John Stultz
parent 198850d2a1
commit 4d358df975
15 changed files with 2341 additions and 0 deletions
@@ -21,6 +21,7 @@
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/iommu.h>
#include <linux/of_address.h>
#include <linux/of.h>
#include <linux/of_irq.h>
+18
View File
@@ -362,4 +362,22 @@ config MTK_IOMMU_V1
if unsure, say N here.
config HISI_IODOMAIN_API
bool
config HISI_IOMMU
bool "Hisilicon IOMMU Support"
select IOMMU_API
select HISI_IODOMAIN_API
help
Hisilicon IOMMU Support.
config HISI_IOMMU_LPAE
bool "Hisilicon IOMMU LPAE Support"
select IOMMU_API
select IODOMAIN_API
select HISI_IOMMU
help
Hisilicon IOMMU Support.
endif # IOMMU_SUPPORT
+2
View File
@@ -27,3 +27,5 @@ obj-$(CONFIG_TEGRA_IOMMU_SMMU) += tegra-smmu.o
obj-$(CONFIG_EXYNOS_IOMMU) += exynos-iommu.o
obj-$(CONFIG_FSL_PAMU) += fsl_pamu.o fsl_pamu_domain.o
obj-$(CONFIG_S390_IOMMU) += s390-iommu.o
obj-$(CONFIG_HISI_IODOMAIN_API) += ion-iommu-map.o
obj-$(CONFIG_HISI_IOMMU_LPAE) += hisi_smmu_lpae.o
+178
View File
@@ -0,0 +1,178 @@
#ifndef HISI_SMMU_H
#define HISI_SMMU_H
/*#define IOMMU_DEBUG*/
#ifdef IOMMU_DEBUG
#define dbg(format, arg...) printk(KERN_ERR "[iommu]"format, ##arg);
#else
#define dbg(format, arg...)
#endif
#define SMMU_PHY_PTRS_PER_PTE (256)
/*#define SMMU_PHY_PTRS_PER_PGD (4096)*/
#define SMMU_PTRS_PER_PGD (4)
#define SMMU_PTRS_PER_PMD (512)
#define SMMU_PTRS_PER_PTE (512)
#define SMMU_PAGE_SHIFT (12)
#define PAGE_TABLE_ADDR_MASK (UL(0xFFFFFFF) << SMMU_PAGE_SHIFT)
#define SMMU_PAGE_SIZE BIT(SMMU_PAGE_SHIFT)
#define SMMU_PAGE_MASK (~(SMMU_PAGE_SIZE-1))
#define SMMU_PGDIR_SHIFT (30)
#define SMMU_PGDIR_SIZE BIT(SMMU_PGDIR_SHIFT)
#define SMMU_PGDIR_MASK (~(SMMU_PGDIR_SIZE-1))
#define SMMU_PMDIR_SHIFT (21)
#define SMMU_PMDIR_SIZE BIT(SMMU_PMDIR_SHIFT)
#define SMMU_PMDIR_MASK (~(SMMU_PMDIR_SIZE-1))
#define SMMU_PGD_TYPE (BIT(0) | BIT(1))
#define SMMU_PMD_TYPE (BIT(0) | BIT(1))
#define SMMU_PTE_TYPE (BIT(0) | BIT(1))
#define SMMU_PGD_NS BIT(63)
#define SMMU_PMD_NS BIT(63)
#define SMMU_PTE_NS BIT(5)
#define SMMU_PTE_PXN BIT(53) /* Privileged XN */
#define SMMU_PTE_UXN BIT(54) /* User XN */
#define SMMU_PTE_USER BIT(6) /* AP[1] */
#define SMMU_PTE_RDONLY BIT(7) /* AP[2] */
#define SMMU_PTE_SHARED (BIT(8) | BIT(9)) /* SH[1:0], inner shareable */
#define SMMU_PTE_AF BIT(10) /* Access Flag */
#define SMMU_PTE_NG BIT(11) /* nG */
#define SMMU_PTE_ATTRINDX(t) ((t) << 2)
/*
* Memory types available.
* USED BY A7
*/
#define HISI_MT_NORMAL 0
#define HISI_MT_NORMAL_CACHE 4
#define HISI_MT_NORMAL_NC 5
#define HISI_MT_DEVICE_nGnRE 6
#define SMMU_PAGE_DEFAULT (SMMU_PTE_TYPE | SMMU_PTE_AF | SMMU_PTE_SHARED)
#define SMMU_PROT_DEVICE_nGnRE (SMMU_PAGE_DEFAULT | SMMU_PTE_PXN | \
SMMU_PTE_UXN | SMMU_PTE_ATTRINDX(HISI_MT_DEVICE_nGnRE))
#define SMMU_PROT_NORMAL_CACHE (SMMU_PAGE_DEFAULT | SMMU_PTE_PXN | \
SMMU_PTE_UXN | SMMU_PTE_ATTRINDX(HISI_MT_NORMAL_CACHE))
#define SMMU_PROT_NORMAL_NC (SMMU_PAGE_DEFAULT | SMMU_PTE_PXN | \
SMMU_PTE_UXN | SMMU_PTE_ATTRINDX(HISI_MT_NORMAL_NC))
#define SMMU_PROT_NORMAL (SMMU_PAGE_DEFAULT | SMMU_PTE_PXN | \
SMMU_PTE_UXN | SMMU_PTE_ATTRINDX(HISI_MT_NORMAL))
#define SMMU_PAGE_READWRITE (SMMU_PAGE_DEFAULT | SMMU_PTE_USER | \
SMMU_PTE_NG | SMMU_PTE_PXN | SMMU_PTE_UXN)
#define SMMU_PAGE_READONLY (SMMU_PAGE_DEFAULT | SMMU_PTE_USER | \
SMMU_PTE_RDONLY | SMMU_PTE_NG | SMMU_PTE_PXN | SMMU_PTE_UXN)
#define SMMU_PAGE_READONLY_EXEC (SMMU_PAGE_DEFAULT | SMMU_PTE_USER | \
SMMU_PTE_NG)
#define smmu_pte_index(addr) (((addr) >> SMMU_PAGE_SHIFT) & (SMMU_PTRS_PER_PTE - 1))
#define smmu_pmd_index(addr) (((addr) >> SMMU_PMDIR_SHIFT) & (SMMU_PTRS_PER_PMD - 1))
#define smmu_pgd_index(addr) (((addr) >> SMMU_PGDIR_SHIFT) & (SMMU_PTRS_PER_PGD - 1))
#define SMMU_PAGE_ALIGN(addr) ALIGN(addr, PAGE_SIZE)
typedef u64 smmu_pgd_t;
typedef u64 smmu_pmd_t;
typedef u64 smmu_pte_t;
/*smmu device object*/
struct hisi_smmu_device_lpae {
struct device *dev ;
struct list_head domain_list;
unsigned int ref_count;
spinlock_t lock;
unsigned long va_pgtable_addr;
phys_addr_t smmu_phy_pgtable_addr;
smmu_pgd_t *smmu_pgd;
};
struct hisi_map_tile_position_lpae {
struct scatterlist *sg ;
unsigned long offset;
};
extern struct hisi_smmu_device_lpae *hisi_smmu_dev;
static inline unsigned int smmu_pgd_none_lpae(smmu_pgd_t pgd) {
return !(pgd ? pgd : 0);
}
static inline unsigned int smmu_pmd_none_lpae(smmu_pmd_t pmd) {
return !(pmd ? pmd : 0);
}
static inline unsigned int smmu_pte_none_lpae(smmu_pte_t pte) {
return !(pte ? pte : 0);
}
static inline unsigned int pte_is_valid_lpae(smmu_pte_t *ptep) {
return (unsigned int)((*(ptep)&SMMU_PTE_TYPE) ? 1 : 0);
}
/* Find an entry in the second-level page table.. */
static inline void *smmu_pmd_page_vaddr_lpae(smmu_pmd_t *pgd)
{
return phys_to_virt(*pgd & PAGE_TABLE_ADDR_MASK);
}
/* Find an entry in the third-level page table.. */
static inline void *smmu_pte_page_vaddr_lpae(smmu_pmd_t *pmd)
{
return phys_to_virt(*pmd & PAGE_TABLE_ADDR_MASK);
}
/*fill the pgd entry, pgd value must be 64bit */
static inline void smmu_set_pgd_lpae(smmu_pgd_t *pgdp, u64 pgd)
{
*pgdp = pgd;
dsb(ishst);
isb();
}
/*fill the pmd entry, pgd value must be 64bit */
static inline void smmu_set_pmd_lpae(smmu_pgd_t *pmdp, u64 pmd)
{
dbg("smmu_set_pmd_lpae: pmd = 0x%lx \n", pmd);
*pmdp = pmd;
dsb(ishst);
isb();
}
static inline void smmu_pmd_populate_lpae(smmu_pmd_t *pmdp, pgtable_t ptep, pgdval_t prot)
{
smmu_set_pmd_lpae(pmdp, (u64)(page_to_phys(ptep) | prot));
}
static inline void smmu_pgd_populate_lpae(smmu_pgd_t *pgdp, pgtable_t pmdp, pgdval_t prot)
{
smmu_set_pgd_lpae(pgdp, (u64)(page_to_phys(pmdp) | prot));
}
static inline unsigned long smmu_pgd_addr_end_lpae(unsigned long addr, unsigned long end)
{
unsigned long boundary = (addr + SMMU_PGDIR_SIZE) & SMMU_PGDIR_MASK;
return (boundary - 1 < end - 1) ? boundary : end;
}
static inline unsigned long smmu_pmd_addr_end_lpae(unsigned long addr, unsigned long end)
{
unsigned long boundary = (addr + SMMU_PMDIR_SIZE) & SMMU_PMDIR_MASK;
return (boundary - 1 < end - 1) ? boundary : end;
}
int hisi_smmu_handle_mapping_lpae(struct iommu_domain *domain,
unsigned long iova, phys_addr_t paddr,
size_t size, int prot);
unsigned int hisi_smmu_handle_unmapping_lpae(struct iommu_domain *domain,
unsigned long iova, size_t size);
#endif
+849
View File
@@ -0,0 +1,849 @@
/*
* hisi_smmu_lpae.c -- 3 layer pagetable
*
* Copyright (c) 2014 Huawei Technologies CO., Ltd.
*
* 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.
*/
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iommu.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/sizes.h>
#include <linux/spinlock.h>
#include <asm/pgalloc.h>
#include <linux/debugfs.h>
#include <linux/hisi/hisi-iommu.h>
#include <linux/uaccess.h>
#include <linux/bitops.h>
#include "hisi_smmu.h"
struct hisi_smmu_device_lpae *hisi_smmu_dev;
/*transfer 64bit pte table pointer to struct page*/
static pgtable_t smmu_pgd_to_pte_lpae(unsigned int ppte_table)
{
unsigned long page_table_addr;
if (!ppte_table) {
dbg("error: the pointer of pte_table is NULL\n");
return NULL;
}
page_table_addr = (unsigned long)ppte_table;
return phys_to_page(page_table_addr);
}
/*transfer 64bit pte table pointer to struct page*/
static pgtable_t smmu_pmd_to_pte_lpae(unsigned long ppte_table)
{
struct page *table = NULL;
if (!ppte_table) {
dbg("error: the pointer of pte_table is NULL\n");
return NULL;
}
table = phys_to_page(ppte_table);
return table;
}
static int get_domain_data_lpae(struct device_node *np,
struct iommu_domain_data *data)
{
unsigned long long align;
struct device_node *node = NULL;
int ret = 0;
data->phy_pgd_base = hisi_smmu_dev->smmu_phy_pgtable_addr;
if (np) {
node = of_find_node_by_name(np, "iommu_info");
if (!node) {
dbg("find iommu_info node error\n");
return -ENODEV;
}
ret = of_property_read_u32(node, "start-addr",
&data->iova_start);
if (ret) {
dbg("read iova start address error\n");
goto read_error;
}
ret = of_property_read_u32(node, "size", &data->iova_size);
if (ret) {
dbg("read iova size error\n");
goto read_error;
}
ret = of_property_read_u64(node, "iova-align", &align);
if (!ret)
data->iova_align = (unsigned long)align;
else
data->iova_align = SZ_256K;
pr_err("%s:start_addr 0x%x, size 0x%x align 0x%lx\n",
__func__, data->iova_start,
data->iova_size, data->iova_align);
}
return 0;
read_error:
return ret;
}
static struct iommu_domain
*hisi_smmu_domain_alloc_lpae(unsigned iommu_domain_type)
{
struct iommu_domain *domain;
if (iommu_domain_type != IOMMU_DOMAIN_UNMANAGED)
return NULL;
domain = kzalloc(sizeof(*domain), GFP_KERNEL);
if (!domain) {
pr_err("%s: fail to kzalloc %lu bytes\n",
__func__, sizeof(*domain));
}
return domain;
}
static void hisi_smmu_flush_pgtable_lpae(void *addr, size_t size)
{
__flush_dcache_area(addr, size);
}
static void hisi_smmu_free_ptes_lpae(smmu_pgd_t pmd)
{
pgtable_t table = smmu_pgd_to_pte_lpae(pmd);
if (!table) {
dbg("pte table is null\n");
return;
}
__free_page(table);
smmu_set_pmd_lpae(&pmd, 0);
}
static void hisi_smmu_free_pmds_lpae(smmu_pgd_t pgd)
{
pgtable_t table = smmu_pmd_to_pte_lpae(pgd);
if (!table) {
dbg("pte table is null\n");
return;
}
__free_page(table);
smmu_set_pgd_lpae(&pgd, 0);
}
static void hisi_smmu_free_pgtables_lpae(unsigned long *page_table_addr)
{
int i, j;
smmu_pgd_t *pgd;
smmu_pmd_t *pmd;
unsigned long flags;
pgd = (smmu_pgd_t *)page_table_addr;
pmd = (smmu_pmd_t *)page_table_addr;
spin_lock_irqsave(&hisi_smmu_dev->lock, flags);
for (i = 0; i < SMMU_PTRS_PER_PGD; ++i) {
if ((smmu_pgd_none_lpae(*pgd)) & (smmu_pmd_none_lpae(*pmd)))
continue;
for (j = 0; j < SMMU_PTRS_PER_PMD; ++j) {
hisi_smmu_free_pmds_lpae(*pgd);
pmd++;
}
hisi_smmu_free_ptes_lpae(*pmd);
pgd++;
}
memset((void *)page_table_addr, 0, PAGE_SIZE);
spin_unlock_irqrestore(&hisi_smmu_dev->lock, flags);
}
static void hisi_smmu_domain_free_lpae(struct iommu_domain *domain)
{
if (list_empty(&hisi_smmu_dev->domain_list))
hisi_smmu_free_pgtables_lpae((unsigned long *)
hisi_smmu_dev->va_pgtable_addr);
kfree(domain);
}
static int hisi_smmu_alloc_init_pte_lpae(smmu_pmd_t *ppmd,
unsigned long addr, unsigned long end,
unsigned long pfn, u64 prot, unsigned long *flags)
{
smmu_pte_t *pte, *start;
pgtable_t table;
u64 pteval = SMMU_PTE_TYPE;
if (!smmu_pmd_none_lpae(*ppmd))
goto pte_ready;
/* Allocate a new set of tables */
spin_unlock_irqrestore(&hisi_smmu_dev->lock, *flags);
table = alloc_page(GFP_KERNEL | __GFP_ZERO | __GFP_DMA);
spin_lock_irqsave(&hisi_smmu_dev->lock, *flags);
if (!table) {
dbg("%s: alloc page fail\n", __func__);
return -ENOMEM;
}
if (smmu_pmd_none_lpae(*ppmd)) {
hisi_smmu_flush_pgtable_lpae(page_address(table),
SMMU_PAGE_SIZE);
smmu_pmd_populate_lpae(ppmd, table, SMMU_PMD_TYPE|SMMU_PMD_NS);
hisi_smmu_flush_pgtable_lpae(ppmd, sizeof(*ppmd));
} else
__free_page(table);
pte_ready:
if (prot & IOMMU_SEC)
*ppmd &= (~SMMU_PMD_NS);
start = (smmu_pte_t *)smmu_pte_page_vaddr_lpae(ppmd)
+ smmu_pte_index(addr);
pte = start;
if (!prot) {
pteval |= SMMU_PROT_NORMAL;
pteval |= SMMU_PTE_NS;
} else {
if (prot & IOMMU_DEVICE) {
pteval |= SMMU_PROT_DEVICE_nGnRE;
} else {
if (prot & IOMMU_CACHE)
pteval |= SMMU_PROT_NORMAL_CACHE;
else
pteval |= SMMU_PROT_NORMAL_NC;
if ((prot & IOMMU_READ) && (prot & IOMMU_WRITE))
pteval |= SMMU_PAGE_READWRITE;
else if ((prot & IOMMU_READ) && !(prot & IOMMU_WRITE))
pteval |= SMMU_PAGE_READONLY;
else
WARN_ON("you do not set read attribute!");
if (prot & IOMMU_EXEC) {
pteval |= SMMU_PAGE_READONLY_EXEC;
pteval &= ~(SMMU_PTE_PXN | SMMU_PTE_UXN);
}
}
if (prot & IOMMU_SEC)
pteval &= (~SMMU_PTE_NS);
else
pteval |= SMMU_PTE_NS;
}
do {
if (!pte_is_valid_lpae(pte))
*pte = (u64)(__pfn_to_phys(pfn)|pteval);
else
WARN_ONCE(1, "map to same VA more times!\n");
pte++;
pfn++;
addr += SMMU_PAGE_SIZE;
} while (addr < end);
hisi_smmu_flush_pgtable_lpae(start, sizeof(*pte) * (pte - start));
return 0;
}
static int hisi_smmu_alloc_init_pmd_lpae(smmu_pgd_t *ppgd,
unsigned long addr, unsigned long end,
unsigned long paddr, int prot, unsigned long *flags)
{
int ret = 0;
smmu_pmd_t *ppmd, *start;
u64 next;
pgtable_t table;
if (!smmu_pgd_none_lpae(*ppgd))
goto pmd_ready;
/* Allocate a new set of tables */
spin_unlock_irqrestore(&hisi_smmu_dev->lock, *flags);
table = alloc_page(GFP_KERNEL | __GFP_ZERO | __GFP_DMA);
spin_lock_irqsave(&hisi_smmu_dev->lock, *flags);
if (!table) {
dbg("%s: alloc page fail\n", __func__);
return -ENOMEM;
}
if (smmu_pgd_none_lpae(*ppgd)) {
hisi_smmu_flush_pgtable_lpae(page_address(table),
SMMU_PAGE_SIZE);
smmu_pgd_populate_lpae(ppgd, table, SMMU_PGD_TYPE|SMMU_PGD_NS);
hisi_smmu_flush_pgtable_lpae(ppgd, sizeof(*ppgd));
} else
__free_page(table);
pmd_ready:
if (prot & IOMMU_SEC)
*ppgd &= (~SMMU_PGD_NS);
start = (smmu_pmd_t *)smmu_pmd_page_vaddr_lpae(ppgd)
+ smmu_pmd_index(addr);
ppmd = start;
do {
next = smmu_pmd_addr_end_lpae(addr, end);
ret = hisi_smmu_alloc_init_pte_lpae(ppmd,
addr, next, __phys_to_pfn(paddr), prot, flags);
if (ret)
goto error;
paddr += (next - addr);
addr = next;
} while (ppmd++, addr < end);
error:
return ret;
}
int hisi_smmu_handle_mapping_lpae(struct iommu_domain *domain,
unsigned long iova, phys_addr_t paddr,
size_t size, int prot)
{
int ret;
unsigned long end;
unsigned long next;
unsigned long flags;
smmu_pgd_t *pgd = (smmu_pgd_t *)hisi_smmu_dev->va_pgtable_addr;
if (!pgd) {
dbg("pgd is null\n");
return -EINVAL;
}
iova = ALIGN(iova, SMMU_PAGE_SIZE);
size = ALIGN(size, SMMU_PAGE_SIZE);
spin_lock_irqsave(&hisi_smmu_dev->lock, flags);
pgd += smmu_pgd_index(iova);
end = iova + size;
do {
next = smmu_pgd_addr_end_lpae(iova, end);
ret = hisi_smmu_alloc_init_pmd_lpae(pgd,
iova, next, paddr, prot, &flags);
if (ret)
goto out_unlock;
paddr += next - iova;
iova = next;
} while (pgd++, iova < end);
out_unlock:
spin_unlock_irqrestore(&hisi_smmu_dev->lock, flags);
return ret;
}
static int hisi_smmu_map_lpae(struct iommu_domain *domain,
unsigned long iova,
phys_addr_t paddr, size_t size,
int prot)
{
unsigned long max_iova;
struct iommu_domain_data *data;
if (!domain) {
dbg("domain is null\n");
return -ENODEV;
}
data = domain->priv;
max_iova = data->iova_start + data->iova_size;
if (iova < data->iova_start) {
dbg("iova failed: iova = 0x%lx, start = 0x%8x\n",
iova, data->iova_start);
goto error;
}
if ((iova+size) > max_iova) {
dbg("iova out of domain range, iova+size=0x%lx, end=0x%lx\n",
iova+size, max_iova);
goto error;
}
return hisi_smmu_handle_mapping_lpae(domain, iova, paddr, size, prot);
error:
dbg("iova is not in this range\n");
return -EINVAL;
}
static unsigned int hisi_smmu_clear_pte_lpae(smmu_pgd_t *pmdp,
unsigned int iova, unsigned int end)
{
smmu_pte_t *ptep = NULL;
smmu_pte_t *ppte = NULL;
unsigned int size = end - iova;
ptep = smmu_pte_page_vaddr_lpae(pmdp);
ppte = ptep + smmu_pte_index(iova);
if (!!size)
memset(ppte, 0x0, (size / SMMU_PAGE_SIZE) * sizeof(*ppte));
return size;
}
static unsigned int hisi_smmu_clear_pmd_lpae(smmu_pgd_t *pgdp,
unsigned int iova, unsigned int end)
{
smmu_pmd_t *pmdp = NULL;
smmu_pmd_t *ppmd = NULL;
unsigned int next = 0;
unsigned int size = end - iova;
pmdp = smmu_pmd_page_vaddr_lpae(pgdp);
ppmd = pmdp + smmu_pmd_index(iova);
do {
next = smmu_pmd_addr_end_lpae(iova, end);
hisi_smmu_clear_pte_lpae(ppmd, iova, next);
iova = next;
dbg("%s: iova=0x%lx, end=0x%lx\n", __func__, iova, end);
} while (ppmd++, iova < end);
return size;
}
unsigned int hisi_smmu_handle_unmapping_lpae(struct iommu_domain *domain,
unsigned long iova, size_t size)
{
smmu_pgd_t *pgdp = NULL;
unsigned int end = 0;
unsigned int next = 0;
unsigned int unmap_size = 0;
unsigned long flags;
iova = SMMU_PAGE_ALIGN(iova);
size = SMMU_PAGE_ALIGN(size);
pgdp = (smmu_pgd_t *)hisi_smmu_dev->va_pgtable_addr;
end = iova + size;
dbg("%s:end=0x%x\n", __func__, end);
pgdp += smmu_pgd_index(iova);
spin_lock_irqsave(&hisi_smmu_dev->lock, flags);
do {
next = smmu_pgd_addr_end_lpae(iova, end);
unmap_size += hisi_smmu_clear_pmd_lpae(pgdp, iova, next);
iova = next;
dbg("%s: pgdp=%p, iova=0x%lx\n", __func__, pgdp, iova);
} while (pgdp++, iova < end);
spin_unlock_irqrestore(&hisi_smmu_dev->lock, flags);
return unmap_size;
}
static size_t hisi_smmu_unmap_lpae(struct iommu_domain *domain,
unsigned long iova, size_t size)
{
unsigned long max_iova;
unsigned int ret;
struct iommu_domain_data *data;
if (!domain) {
dbg("domain is null\n");
return -ENODEV;
}
data = domain->priv;
/*caculate the max io virtual address */
max_iova = data->iova_start + data->iova_size;
/*check the iova */
if (iova < data->iova_start)
goto error;
if ((iova+size) > max_iova) {
dbg("iova out of domain range, iova+size=0x%lx, end=0x%lx\n",
iova+size, max_iova);
goto error;
}
/*unmapping the range of iova*/
ret = hisi_smmu_handle_unmapping_lpae(domain, iova, size);
if (ret == size) {
dbg("%s:unmap size:0x%x\n", __func__, (unsigned int)size);
return size;
} else {
return 0;
}
error:
dbg("%s:the range of io address is wrong\n", __func__);
return -EINVAL;
}
static phys_addr_t hisi_smmu_iova_to_phys_lpae(
struct iommu_domain *domain, dma_addr_t iova)
{
smmu_pgd_t *pgdp, pgd;
smmu_pmd_t pmd;
smmu_pte_t pte;
pgdp = (smmu_pgd_t *)hisi_smmu_dev->va_pgtable_addr;
if (!pgdp)
return 0;
pgd = *(pgdp + smmu_pgd_index(iova));
if (smmu_pgd_none_lpae(pgd))
return 0;
pmd = *((smmu_pmd_t *)smmu_pmd_page_vaddr_lpae(&pgd) +
smmu_pmd_index(iova));
if (smmu_pmd_none_lpae(pmd))
return 0;
pte = *((u64 *)smmu_pte_page_vaddr_lpae(&pmd) + smmu_pte_index(iova));
if (smmu_pte_none_lpae(pte))
return 0;
return __pfn_to_phys(pte_pfn(__pte(pte))) | (iova & ~SMMU_PAGE_MASK);
}
static int hisi_attach_dev_lpae(struct iommu_domain *domain, struct device *dev)
{
struct device_node *np = dev->of_node;
int ret = 0;
struct iommu_domain_data *iommu_info = NULL;
iommu_info = kzalloc(sizeof(struct iommu_domain_data), GFP_KERNEL);
if (!iommu_info) {
dbg("alloc iommu_domain_data fail\n");
return -EINVAL;
}
list_add(&iommu_info->list, &hisi_smmu_dev->domain_list);
domain->priv = iommu_info;
ret = get_domain_data_lpae(np, domain->priv);
return ret;
}
static void hisi_detach_dev_lpae(struct iommu_domain *domain,
struct device *dev)
{
struct iommu_domain_data *data;
data = (struct iommu_domain_data *)domain->priv;
if (data) {
list_del(&data->list);
domain->priv = NULL;
kfree(data);
} else {
dbg("%s:error! data entry has been delected\n", __func__);
}
}
static dma_addr_t get_phys_addr_lpae(struct scatterlist *sg)
{
dma_addr_t dma_addr = sg_dma_address(sg);
if (!dma_addr)
dma_addr = sg_phys(sg);
return dma_addr;
}
int iommu_map_tile(struct iommu_domain *domain, unsigned long iova,
struct scatterlist *sg, size_t size, int prot,
struct tile_format *format)
{
if (unlikely(!(domain->ops->map_tile)))
return -ENODEV;
BUG_ON(iova & (~PAGE_MASK));
return domain->ops->map_tile(domain, iova, sg, size, prot, format);
}
int iommu_unmap_tile(struct iommu_domain *domain, unsigned long iova,
size_t size)
{
if (unlikely(!(domain->ops->unmap_tile)))
return -ENODEV;
BUG_ON(iova & (~PAGE_MASK));
return domain->ops->unmap_tile(domain, iova, size);
}
/*
*iova: the start address for tile mapping
*size: the physical memory size
*sg: the node of scatter list where are the start node of physical memory
*sg_offset:the physical memory offset in the sg node ,where is the start
position of physical memory
*port: the pape property of virtual memory
* this function complete one row mapping.
*/
static size_t hisi_map_tile_row_lpae(struct iommu_domain *domain, unsigned long
iova, size_t size, struct scatterlist *sg, size_t sg_offset,
struct hisi_map_tile_position_lpae *map_position,
unsigned int prot){
unsigned long map_size; /*the memory size that will be mapped*/
unsigned long phys_addr;
unsigned long mapped_size = 0; /*memory size that has been mapped*/
int ret;
while (1) {
/*
*get the remain memory,if current sg node is not enough memory,
*we map the remain memory firstly.
*/
map_size = size - mapped_size;
if (map_size > (sg->length - sg_offset))
map_size = (sg->length - sg_offset);
/*get the start physical address*/
phys_addr = (unsigned long)get_phys_addr_lpae(sg) + sg_offset;
ret = hisi_smmu_map_lpae(domain,
iova + mapped_size, phys_addr, map_size, prot);
if (ret) {
dbg("[%s] hisi_smmu_map failed!\n", __func__);
break;
}
/*update mapped memory size*/
mapped_size += map_size;
/*
* if finished mapping,
* we update the memory offset of current node and
* save the memory position. otherwise we clean the sg_offset
* to zero and get next sg node.
*/
if (mapped_size < size) {
sg_offset = 0;
sg = sg_next(sg);
if (!sg) {
dbg("[%s] phy memory not enough\n", __func__);
break;
}
} else {
sg_offset += map_size;
/*if physcial memory of this node is exhausted,
* we choose next node
*/
if (sg_offset == sg->length) {
sg_offset = 0;
sg = sg_next(sg);
}
break;
}
}
/*save current position*/
map_position->sg = sg;
map_position->offset = sg_offset;
return mapped_size;
}
/*
*domain:the iommu domain for mapping
*iova:the start virtual address
*sg: the scatter list of physical memory
*size:the total size of all virtual memory
*port:the property of page table of virtual memory
*format:the parameter of tile mapping
*this function map physical memory in tile mode
*/
static int hisi_smmu_map_tile_lpae(struct iommu_domain *domain,
unsigned long iova,
struct scatterlist *sg, size_t size, int prot,
struct tile_format *format){
unsigned int phys_length;
struct scatterlist *sg_node;
unsigned int row_number, row;
unsigned int size_virt, size_phys;
unsigned int sg_offset;
int ret = size;
unsigned int mapped_size, header_size;
struct hisi_map_tile_position_lpae map_position;
/* calculate the whole length of phys mem */
for (phys_length = 0, sg_node = sg; sg_node; sg_node = sg_next(sg_node))
phys_length += ALIGN(sg_node->length, PAGE_SIZE);
header_size = format->header_size;
/* calculate the number of raws*/
row_number = ((phys_length - header_size) >> PAGE_SHIFT)
/ format->phys_page_line;
dbg("phys_length: 0x%x, rows: 0x%x, header_size: 0x%x\n",
phys_length, row_number, header_size);
/*caculate the need physical memory and virtual memory for one row*/
size_phys = (format->phys_page_line * PAGE_SIZE);
size_virt = (format->virt_page_line * PAGE_SIZE);
sg_offset = 0;
sg_node = sg;
/*set start position*/
map_position.sg = sg;
map_position.offset = 0;
/*map header*/
if (header_size) {
mapped_size = hisi_map_tile_row_lpae(domain, iova,
header_size, sg_node,
sg_offset, &map_position,
prot);
if (mapped_size != header_size) {
WARN(1, "map head fail\n");
ret = -EINVAL;
goto error;
}
iova += ALIGN(header_size, size_virt);
}
/* map row by row */
for (row = 0; row < row_number; row++) {
/* get physical memory position */
if (map_position.sg) {
sg_node = map_position.sg;
sg_offset = map_position.offset;
} else {
dbg("[%s]:physical memory is not enough\n", __func__);
break;
}
/* map one row*/
mapped_size = hisi_map_tile_row_lpae(domain,
iova + (size_virt * row),
size_phys, sg_node, sg_offset,
&map_position, prot);
if (mapped_size != size_phys) {
WARN(1, "hisi_map_tile_row failed!\n");
ret = -EINVAL;
break;
}
};
error:
return ret;
}
static size_t hisi_smmu_unmap_tile_lpae(struct iommu_domain *domain,
unsigned long iova, size_t size)
{
return hisi_smmu_unmap_lpae(domain, iova, size);
}
size_t hisi_iommu_map_sg_lpae(struct iommu_domain *domain, unsigned long iova,
struct scatterlist *sg, unsigned int nents, int prot)
{
struct scatterlist *s;
size_t mapped = 0;
unsigned int i, min_pagesz;
int ret;
if (domain->ops->pgsize_bitmap == 0UL)
return 0;
min_pagesz = (unsigned int)1 << __ffs(domain->ops->pgsize_bitmap);
for_each_sg(sg, s, nents, i) {
phys_addr_t phys = page_to_phys(sg_page(s)) + s->offset;
/*
* We are mapping on IOMMU page boundaries, so offset within
* the page must be 0. However, the IOMMU may support pages
* smaller than PAGE_SIZE, so s->offset may still represent
* an offset of that boundary within the CPU page.
*/
if (!IS_ALIGNED(s->offset, min_pagesz))
goto out_err;
ret = hisi_smmu_map_lpae(domain, iova + mapped, phys,
(size_t)s->length, prot);
if (ret)
goto out_err;
mapped += s->length;
}
return mapped;
out_err:
/* undo mappings already done */
hisi_smmu_unmap_lpae(domain, iova, mapped);
return 0;
}
static struct iommu_ops hisi_smmu_ops = {
.domain_alloc = hisi_smmu_domain_alloc_lpae,
.domain_free = hisi_smmu_domain_free_lpae,
.map = hisi_smmu_map_lpae,
.unmap = hisi_smmu_unmap_lpae,
.map_sg = hisi_iommu_map_sg_lpae,
.attach_dev = hisi_attach_dev_lpae,
.detach_dev = hisi_detach_dev_lpae,
.iova_to_phys = hisi_smmu_iova_to_phys_lpae,
.pgsize_bitmap = SMMU_PAGE_SIZE,
.map_tile = hisi_smmu_map_tile_lpae,
.unmap_tile = hisi_smmu_unmap_tile_lpae,
};
static int hisi_smmu_probe_lpae(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
dbg("enter %s\n", __func__);
hisi_smmu_dev = devm_kzalloc(dev,
sizeof(struct hisi_smmu_device_lpae), GFP_KERNEL);
hisi_smmu_dev->smmu_pgd = devm_kzalloc(dev, SZ_64, GFP_KERNEL | __GFP_DMA);
if (!hisi_smmu_dev)
goto smmu_device_error;
hisi_smmu_dev->dev = dev;
INIT_LIST_HEAD(&hisi_smmu_dev->domain_list);
spin_lock_init(&hisi_smmu_dev->lock);
hisi_smmu_dev->smmu_pgd = (smmu_pgd_t *)(ALIGN((unsigned long)(hisi_smmu_dev->smmu_pgd), SZ_32));
hisi_smmu_dev->smmu_phy_pgtable_addr =
virt_to_phys(hisi_smmu_dev->smmu_pgd);
printk(KERN_ERR "%s, smmu_phy_pgtable_addr is = %llx\n", __func__, hisi_smmu_dev->smmu_phy_pgtable_addr);
hisi_smmu_dev->va_pgtable_addr = (unsigned long)(hisi_smmu_dev->smmu_pgd);
bus_set_iommu(&platform_bus_type, &hisi_smmu_ops);
return 0;
smmu_device_error:
return -ENOMEM;
}
static int hisi_smmu_remove_lpae(struct platform_device *pdev)
{
return 0;
}
static const struct of_device_id hisi_smmu_of_match_lpae[] = {
{ .compatible = "hisi,hisi-smmu-lpae"},
{ },
};
MODULE_DEVICE_TABLE(of, hisi_smmu_of_match_lpae);
static struct platform_driver hisi_smmu_driver_lpae = {
.driver = {
.owner = THIS_MODULE,
.name = "hisi-smmu-lpae",
.of_match_table = of_match_ptr(hisi_smmu_of_match_lpae),
},
.probe = hisi_smmu_probe_lpae,
.remove = hisi_smmu_remove_lpae,
};
static int __init hisi_smmu_init_lpae(void)
{
int ret = 0;
ret = platform_driver_register(&hisi_smmu_driver_lpae);
return ret;
}
static void __exit hisi_smmu_exit_lpae(void)
{
return platform_driver_unregister(&hisi_smmu_driver_lpae);
}
subsys_initcall(hisi_smmu_init_lpae);
module_exit(hisi_smmu_exit_lpae);
MODULE_DESCRIPTION("IOMMU API for HI3660 architected SMMU implementations");
MODULE_AUTHOR("huawei hisilicon company");
MODULE_LICENSE("GPL v2");
+40
View File
@@ -0,0 +1,40 @@
# Hisilicon IOMMU support
config HISI_IODOMAIN_API
bool
config HISI_IOMMU
bool "Hisilicon IOMMU Support"
select IOMMU_API
select HISI_IODOMAIN_API
help
Hisilicon IOMMU Support.
config HISI_IOMMU_LPAE
bool "Hisilicon IOMMU LPAE Support"
select IOMMU_API
depends on HISI_IOMMU
help
Hisilicon IOMMU Support.
config HISI_IOMMU_LEGACY
bool "Hisilicon IOMMU SECOND LEVEL PAGE TABLE Support"
select IOMMU_API
depends on HISI_IOMMU && !HISI_IOMMU_LPAE
help
Hisilicon IOMMU Support.
config HISI_IOMMU_COMPACT
bool "Hisilicon IOMMU compact linux4.1"
help
On linux4.1, IOMMU framework has some change point,
you need compact it.If you build on linux4.1 and need
hisilicon iommu,you should select this 'y'.
Then, you should select this 'n'.
config HISI_IOMMU_TEST
bool "Hisilicon IOMMU TEST Support"
depends on HISI_IOMMU_LPAE
select IOMMU_API
help
Hisilicon IOMMU Support.
+5
View File
@@ -0,0 +1,5 @@
obj-$(CONFIG_HISI_IOMMU_LEGACY) += hisi_smmu.o
obj-$(CONFIG_HISI_IODOMAIN_API) += ion-iommu-map.o
obj-$(CONFIG_HISI_IOMMU_LPAE) += hisi_smmu_lpae.o
obj-$(CONFIG_HISI_IOMMU_TEST) += hisi_smmu_test.o
obj-$(CONFIG_HISI_IOMMU_TEST) += hisi_smmu_unittest.o
+387
View File
@@ -0,0 +1,387 @@
/*
* Copyright (C) 2013-2013 ...
* ...
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/iommu.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/hisi/hisi_ion.h>
#include <linux/hisi/ion-iommu.h>
#include <linux/hisi/hisi-iommu.h>
#include <linux/of.h>
/*#define IOMMU_DEBUG*/
#ifdef IOMMU_DEBUG
#define D(format, arg...) \
do {\
printk(KERN_ERR "[iommutest] " format, ##arg);\
} while (0)
#else
#define D(format, arg...)
#endif
#undef IOMMU_DEBUG
static struct sg_table *table;
enum {
IOMMU_TEST,
ION_TEST,
TILE_TEST
};
struct smmu_tester{
struct ion_handle *ion_handle;
struct ion_client *ion_client;
struct iommu_domain *domain;
};
struct iommu_page_info {
struct page *page;
unsigned int order;
struct list_head list;
};
static struct iommu_domain_data *info;
static struct sg_table *table;
struct smmu_tester *smmu_tester;
/*
*iova:the start io virtual address.
*size:the size of io virtual memory.
*this function unmap a section io virtual memory
*/
static int iommu_unmap_test(unsigned int iova, unsigned int size)
{
int ret;
if (!smmu_tester->domain) {
D("domain is null");
return -EINVAL;
}
/* iommu unmap */
ret = iommu_unmap(smmu_tester->domain, iova, size);
if (!ret) {
D("hisi iommu unmap domain failed!\n");
}
return ret;
}
/*this function do iommu map*/
ssize_t iommu_map_test(unsigned int start , unsigned int size)
{
unsigned int iova_start = 0;
unsigned int map_size = 0;
unsigned int i;
struct scatterlist *sg = NULL ;
ktime_t time_start, time_end;
unsigned long diff;
int ret = 0;
/*get parameter from buffer*/
iova_start = start;
map_size = size;
D("%s: iova_start: 0x%x, size: 0x%x ", __func__, iova_start, map_size);
time_start = ktime_get();
if (smmu_tester->domain && table) {
for_each_sg(table->sgl, sg, table->nents, i){
ret = iommu_map(smmu_tester->domain, iova_start, page_to_phys(sg_page(sg)), sg->length, IOMMU_READ|IOMMU_WRITE|IOMMU_CACHE);
if (ret) {
D("failed to map devmem: %d\n",ret);
goto out;
}
iova_start += sg->length;
}
}
time_end = ktime_get();
diff = ktime_to_us(ktime_sub(time_end,time_start));
D("%s: mapping time is 0x%lx \n", __func__, diff);
return size;
out:
return -ENOSPC;
}
/*get physical address according to io vritual address*/
ssize_t iova_to_phy(void)
{
unsigned int iova_addr = 0;
unsigned int phys_addr;
phys_addr = iommu_iova_to_phys(smmu_tester->domain, iova_addr);
D("iova_addr=0x%x and phys_addr=0x%x\n", iova_addr, phys_addr);
return 0;
}
/*free physical memory*/
static int free_memory(void)
{
int i;
struct scatterlist *sg = NULL;
unsigned int mem_size = 0;
if (table) {
for_each_sg(table->sgl, sg, table->nents, i) {
__free_pages(sg_page(sg), get_order(sg->length));
mem_size += sg->length;
}
D("%s:free total memory 0x%x \n", __func__, mem_size);
sg_free_table(table);
kfree(table);
}
table = NULL;
return 0;
}
/*
*this function allocate physical memory,
*and make them to scatter lista.
*table is global .
*/
static struct iommu_page_info *create_node(void)
{
struct iommu_page_info *info = NULL;
struct page *page = NULL ;
info = kmalloc(sizeof(struct iommu_page_info), GFP_KERNEL);
if (!info) {
D("%s: kmalloc info failed!\n", __func__);
return NULL;
}
page = alloc_pages(GFP_KERNEL, 1);
/*alloc 8kb each
* time*/
if (!page) {
D("alloc page error \n");
kfree(info);
return NULL;
}
info->page = page;
info->order = 0;
INIT_LIST_HEAD(&info->list);
return info;
}
static int alloc_memory (unsigned int size)
{
int map_size = 0;
unsigned int sum = 0;
struct list_head pages;
struct iommu_page_info *info, *tmp_info;
unsigned int i = 0, ret = 0;
struct scatterlist *sg = NULL;
INIT_LIST_HEAD(&pages);
map_size = size;
if (map_size < 0)
return -EINVAL;
D("%s: map_size=0x%x \n", __func__, map_size);
do {
info = create_node();
if (!info)
goto error;
list_add_tail(&info->list, &pages);
sum += (1 << info->order) *PAGE_SIZE;
i++;
} while (sum < map_size);
table = kzalloc(sizeof(struct sg_table), GFP_KERNEL);
if (!table) {
goto error;
}
ret = sg_alloc_table(table,i, GFP_KERNEL);
if (ret) {
kfree(table);
goto error;
}
sg = table->sgl;
list_for_each_entry_safe(info, tmp_info, &pages, list)
{
struct page *page = info->page;
sg_set_page(sg, page, (1 << info->order)*PAGE_SIZE, 0);
sg = sg_next(sg);
list_del(&info->list);
kfree(info);
}
D("sglist is ok \n");
return map_size;
error:
list_for_each_entry_safe(info, tmp_info, &pages, list)
{
list_del(&info->list);
kfree(info);
}
return 0;
}
/*
*test mapping address from ion device
*/
int test_smmu_ion_tile_map(unsigned int global_map_start, unsigned int global_map_size)
{
int ret;
struct iommu_map_format format = {0};
format.iova_size = global_map_size;
format.phys_page_line = 60;
format.virt_page_line = 64;
format.is_tile = 0x01;
format.prot = 0xff;
smmu_tester->ion_handle = ion_alloc(smmu_tester->ion_client, global_map_size, SZ_4K, ION_HEAP(ION_SYSTEM_HEAP_ID),0x0);
if (IS_ERR(smmu_tester->ion_handle)) {
D("alloc is fail\n");
return 0;
}
ret = ion_map_iommu(smmu_tester->ion_client, smmu_tester->ion_handle, &format);
if (ret)
{
D("ion map iommu is failed\n");
return 0;
}
D("%s end\n", __func__);
return format.iova_start;
}
/*
* test unmapping address from ion device
*/
static int test_smmu_ion_tile_unmap(void)
{
printk(KERN_ERR"%s start\n", __func__);
ion_unmap_iommu(smmu_tester->ion_client, smmu_tester->ion_handle);
return 0;
}
int test_smmu_ion_map(unsigned int global_map_start, unsigned int global_map_size)
{
int ret;
unsigned int size = global_map_size;
struct iommu_map_format format = {0};
unsigned int align = SZ_4K;
format.iova_size = size;
format.prot = 0xff;
smmu_tester->ion_handle = ion_alloc(smmu_tester->ion_client, size,align, ION_HEAP(ION_SYSTEM_HEAP_ID), 0x0);
if (IS_ERR(smmu_tester->ion_handle)) {
pr_err("alloc is fail\n");
return 0;
}
ret = ion_map_iommu(smmu_tester->ion_client, smmu_tester->ion_handle, &format);
if (ret) {
pr_err("ion_map_iommu is failed\n");
return 0;
}
return format.iova_start;
}
int test_smmu_ion_unmap(void)
{
D("%s start\n", __func__);
ion_unmap_iommu(smmu_tester->ion_client, smmu_tester->ion_handle);
return 1;
}
extern void smmu_print_pgtable(void);
extern void set_smmu_param(unsigned int start, unsigned int size);
ssize_t smmu_test_main(int type, unsigned int global_map_size, unsigned int global_map_start)
{
unsigned int ret = 0;
ret = alloc_memory(global_map_size);
if (!ret) {
D("ret = %d\n", ret);
return -ENOMEM;
}
switch (type) {
case IOMMU_TEST:
iommu_map_test(global_map_start, global_map_size);
set_smmu_param(global_map_start, global_map_size);
smmu_print_pgtable();
iommu_unmap_test(global_map_start, global_map_size);
smmu_print_pgtable();
break;
case ION_TEST:
ret = test_smmu_ion_map(global_map_start, global_map_size);
set_smmu_param(ret, global_map_size);
smmu_print_pgtable();
test_smmu_ion_unmap();
smmu_print_pgtable();
break;
case TILE_TEST:
ret = test_smmu_ion_tile_map(global_map_start, global_map_size);
set_smmu_param(ret, global_map_size);
smmu_print_pgtable();
test_smmu_ion_tile_unmap();
smmu_print_pgtable();
break;
default:
return -EINVAL;
break;
};
free_memory();
return 0;
}
static int hisi_iommutest_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
smmu_tester = devm_kzalloc(&pdev->dev, sizeof(struct smmu_tester), GFP_KERNEL);
if (!smmu_tester) {
D("in %s devm_kzalloc is failed\n", __func__);
return -ENOMEM;
}
smmu_tester->ion_client = hisi_ion_client_create("smmu_test");
if (IS_ERR(smmu_tester->ion_client)) {
D("hisi ion client create failed \n");
return -ENODEV;
}
smmu_tester->domain = iommu_domain_alloc(dev->bus);
if (!smmu_tester->domain) {
D("create domain fail \n");
return -ENOMEM;
} else {
iommu_attach_device(smmu_tester->domain, dev);
info = (struct iommu_domain_data *)smmu_tester->domain->priv;
D("%s,iova_start=0x%lx,iova_size=0x%lx \n", __func__,
info->iova_start,
info->iova_size);
}
return 0;
}
static struct of_device_id hisi_smmu_of_table[] = {
{ .compatible = "hisi,hisi-smmu-tester"},
{ },
};
MODULE_DEVICE_TABLE(of, hisi_smmu_of_table);
static struct platform_driver hisi_iommutest_drv = {
.driver = {
.owner = THIS_MODULE,
.name = "hisi-smmu-tester",
.of_match_table = of_match_ptr(hisi_smmu_of_table),
},
.probe = hisi_iommutest_probe,
};
/*the inital function for iommu test module*/
static int __init init_iommu_test(void)
{
int err = -EBUSY;
err = platform_driver_register(&hisi_iommutest_drv);
if (err)
D("register device error \n");
return 0;
}
module_init(init_iommu_test);
+365
View File
@@ -0,0 +1,365 @@
/*
* Copyright (C) 20013-2013 hisilicon. All rights reserved.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/debugfs.h>
#include <linux/genalloc.h>
#include <linux/iommu.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/seq_file.h>
#include <linux/sizes.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/hisi/hisi-iommu.h>
#include <linux/hisi/ion-iommu.h>
#ifdef IOMMU_DEBUG
#define dbg(format, arg...) \
pr_info("[hisi_iommu_domain]"format, ##arg)
#else
#define dbg(format, arg...)
#endif
struct map_result {
unsigned long iova_start;
unsigned long iova_size;
unsigned long iommu_ptb_base;
unsigned long iommu_iova_base;
unsigned long is_tile;
};
struct iommu_domain_data *domain_info;
static struct hisi_iommu_domain *m_hisi_domain;
DEFINE_MUTEX(iova_pool_mutex);
static unsigned long hisi_alloc_iova(struct gen_pool *pool,
unsigned long size, unsigned long align)
{
unsigned long iova = 0;
mutex_lock(&iova_pool_mutex);
iova = gen_pool_alloc(pool, size);
if (!iova) {
mutex_unlock(&iova_pool_mutex);
pr_err("hisi iommu gen_pool_alloc failed! size = %lu\n", size);
return 0;
}
if (align > (1 << pool->min_alloc_order))
WARN(1, "hisi iommu domain cant align to 0x%lx\n", align);
mutex_unlock(&iova_pool_mutex);
return iova;
}
static void hisi_free_iova(struct gen_pool *pool,
unsigned long iova, size_t size)
{
mutex_lock(&iova_pool_mutex);
gen_pool_free(pool, iova, size);
mutex_unlock(&iova_pool_mutex);
}
unsigned long hisi_iommu_alloc_iova(size_t size, unsigned long align)
{
struct hisi_iommu_domain *hisi_domain = m_hisi_domain;
return hisi_alloc_iova(hisi_domain->iova_pool, size, align);
}
EXPORT_SYMBOL_GPL(hisi_iommu_alloc_iova);
void hisi_iommu_free_iova(unsigned long iova, size_t size)
{
int ret;
struct hisi_iommu_domain *hisi_domain = m_hisi_domain;
ret = addr_in_gen_pool(hisi_domain->iova_pool, iova, size);
if(!ret) {
pr_err("%s:illegal para!!iova = %lx, size = %lx\n",
__func__, iova, size);
}
hisi_free_iova(hisi_domain->iova_pool, iova, size);
}
EXPORT_SYMBOL_GPL(hisi_iommu_free_iova);
static struct gen_pool *iova_pool_setup(unsigned long start,
unsigned long size, unsigned long align)
{
struct gen_pool *pool = NULL;
int ret = 0;
pool = gen_pool_create(order_base_2(align), -1);/*lint !e666 */
if (!pool) {
pr_err("Create gen pool failed!\n");
return NULL;
}
/* iova start should not be 0, because return
0 when alloc iova is considered as error */
if (!start)
WARN(1, "iova start should not be 0!\n");
ret = gen_pool_add(pool, start, size, -1);
if (ret) {
pr_err("Gen pool add failed!\n");
gen_pool_destroy(pool);
return NULL;
}
return pool;
}
static void iova_pool_destroy(struct gen_pool *pool)
{
gen_pool_destroy(pool);
}
static int do_iommu_domain_map(struct hisi_iommu_domain *hisi_domain,
struct scatterlist *sgl, struct iommu_map_format *format,
struct map_result *result)
{
int ret;
unsigned long phys_len, iova_size;
unsigned long iova_start;
struct gen_pool *pool;
struct iommu_domain *domain;
struct scatterlist *sg;
struct tile_format fmt;
/* calculate whole phys mem length */
for (phys_len = 0, sg = sgl; sg; sg = sg_next(sg))
phys_len += (unsigned long)ALIGN(sg->length, PAGE_SIZE);
/* get io virtual address size */
if (format->is_tile) {
unsigned long lines;
unsigned long body_size;
body_size = phys_len - format->header_size;
lines = body_size / (format->phys_page_line * PAGE_SIZE);
/*header need more lines virtual space*/
if (format->header_size) {
unsigned long header_size;
header_size = ALIGN(format->header_size,
format->virt_page_line * PAGE_SIZE);
lines += header_size
/ (format->virt_page_line * PAGE_SIZE);
}
iova_size = lines * format->virt_page_line * PAGE_SIZE;
} else {
iova_size = phys_len;
}
/* alloc iova */
pool = hisi_domain->iova_pool;
domain = hisi_domain->domain;
iova_start = hisi_alloc_iova(pool, iova_size, hisi_domain->range.align);
if (!iova_start) {
pr_err("[%s]hisi_alloc_iova alloc size 0x%lx failed!"
"hisi ion pool avail 0x%lx\n",
__func__, iova_size, gen_pool_avail(pool));
return -EINVAL;
}
if (0x100000000 < (iova_start + iova_size)) {
pr_err("hisi iommu can not deal with iova 0x%lx size 0x%lx\n",
iova_start, iova_size);
}
/* do map */
if (format->is_tile) {
fmt.is_tile = format->is_tile;
fmt.phys_page_line = format->phys_page_line;
fmt.virt_page_line = format->virt_page_line;
fmt.header_size = format->header_size;
ret = iommu_map_tile(domain, iova_start,
sgl, iova_size, 0, &fmt);
} else {
ret = iommu_map_sg(domain, iova_start, sgl,
sg_nents(sgl), format->prot);
}
if (ret != iova_size) {
pr_err("[%s]map failed!iova_start = %lx, iova_size = %lx\n",
__func__, iova_start, iova_size);
hisi_free_iova(pool, iova_start, iova_size);
return ret;
}
/* out put result */
result->iova_start = iova_start;
result->iova_size = iova_size;
return 0;
}
int hisi_iommu_map_domain(struct scatterlist *sgl,
struct iommu_map_format *format)
{
int ret = 0;
struct map_result result;
struct hisi_iommu_domain *hisi_domain;
hisi_domain = m_hisi_domain;
memset(&result, 0, sizeof(result));
ret = do_iommu_domain_map(hisi_domain, sgl, format, &result);
if (ret) {
dbg("alloc iova fail\n");
return ret;
}
format->iova_start = result.iova_start;
format->iova_size = result.iova_size;
/* get value which write into iommu register */
return ret;
}
EXPORT_SYMBOL_GPL(hisi_iommu_map_domain);
static int do_iommu_domain_unmap(struct map_result *result)
{
int ret;
unsigned long unmaped_size;
struct hisi_iommu_domain *hisi_domain = m_hisi_domain;
struct gen_pool *pool = hisi_domain->iova_pool;
/* never unmap a zero length address space */
if (!result->iova_size) {
pr_err("[%s]unmap failed! iova_start=%lx, iova_size=%lu\n",
__func__, result->iova_start, result->iova_size);
return -EINVAL;
}
/* unmap tile equals to unmpa range */
if (result->is_tile) {
unmaped_size = iommu_unmap_tile(hisi_domain->domain,
result->iova_start, result->iova_size);
} else {
unmaped_size = iommu_unmap(hisi_domain->domain,
result->iova_start, result->iova_size);
}
if (unmaped_size != result->iova_size) {
dbg("[%s]unmap failed!\n", __func__);
return -EINVAL;
}
/* free iova */
if (pool) {
ret = addr_in_gen_pool(pool, result->iova_start,
result->iova_size);
if(!ret) {
pr_err("[%s]illegal para!!iova = %lx, size = %lx\n",
__func__, result->iova_start, result->iova_size);
}
hisi_free_iova(pool, result->iova_start, result->iova_size);
}
return 0;
}
#ifdef CONFIG_ARM64_64K_PAGES
#error hisi iommu can not deal with 64k pages!
#endif
/**
* Called by ION
*/
int hisi_iommu_unmap_domain(struct iommu_map_format *format)
{
struct map_result result;
result.iova_start = format->iova_start;
result.iova_size = format->iova_size;
result.is_tile = format->is_tile;
return do_iommu_domain_unmap(&result);
}
EXPORT_SYMBOL_GPL(hisi_iommu_unmap_domain);
/*only used to test*/
phys_addr_t hisi_iommu_domain_iova_to_phys(unsigned long iova)
{
struct iommu_domain *domain;
domain = m_hisi_domain->domain;
return iommu_iova_to_phys(domain, iova);
}
EXPORT_SYMBOL_GPL(hisi_iommu_domain_iova_to_phys);
int hisi_ion_enable_iommu(struct platform_device *pdev)
{
int ret;
struct device *dev = &pdev->dev;
struct hisi_iommu_domain *hisi_domain;
pr_info("in %s start\n", __func__);
hisi_domain = kzalloc(sizeof(*hisi_domain), GFP_KERNEL);
if (!hisi_domain) {
dbg("alloc hisi_domain object fail\n");
return -ENOMEM;
}
if (!iommu_present(dev->bus)) {
dbg("iommu not found\n");
kfree(hisi_domain);
return 0;
}
/* create iommu domain */
hisi_domain->domain = iommu_domain_alloc(dev->bus);
if (!hisi_domain->domain) {
ret = -EINVAL;
goto error;
}
iommu_attach_device(hisi_domain->domain, dev);
domain_info = (struct iommu_domain_data *)hisi_domain->domain->priv;
/**
* Current align is 256K
*/
hisi_domain->iova_pool = iova_pool_setup(domain_info->iova_start,
domain_info->iova_size, domain_info->iova_align);
if (!hisi_domain->iova_pool) {
ret = -EINVAL;
goto error;
}
/* this is a global pointer */
m_hisi_domain = hisi_domain;
dbg("in %s end\n", __func__);
return 0;
error:
WARN(1, "hisi_iommu_domain_init failed!\n");
if (hisi_domain->iova_pool)
iova_pool_destroy(hisi_domain->iova_pool);
if (hisi_domain->domain)
iommu_domain_free(hisi_domain->domain);
kfree(hisi_domain);
return ret;
}
EXPORT_SYMBOL(hisi_ion_enable_iommu);
+13
View File
@@ -0,0 +1,13 @@
#ifndef _HI36XX_SMMU_H
#define _HI36XX_SMMU_H
#include <linux/types.h>
struct iommu_domain_data {
unsigned int iova_start;
unsigned int iova_size;
phys_addr_t phy_pgd_base;
unsigned long iova_align;
struct list_head list;
};
#endif
+178
View File
@@ -0,0 +1,178 @@
/*
*
* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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.
*
*/
#ifndef _LINUX_HISI_ION_H
#define _LINUX_HISI_ION_H
#include <linux/ion.h>
#include <linux/sizes.h>
/**
* These are the only ids that should be used for Ion heap ids.
* The ids listed are the order in which allocation will be attempted
* if specified. Don't swap the order of heap ids unless you know what
* you are doing!
* Id's are spaced by purpose to allow new Id's to be inserted in-between (for
* possible fallbacks)
*/
enum ion_heap_ids {
INVALID_HEAP_ID = -1,
ION_SYSTEM_HEAP_ID = 0,
ION_SYSTEM_CONTIG_HEAP_ID = 1,
ION_GRALLOC_HEAP_ID = 2,
ION_DMA_HEAP_ID = 3,
ION_DMA_POOL_HEAP_ID = 4,
ION_CPU_DRAW_HEAP_ID = 5,
ION_CAMERA_HEAP_ID = 6,
ION_OVERLAY_HEAP_ID = 7,
ION_VCODEC_HEAP_ID = 8,
ION_ISP_HEAP_ID = 9,
ION_FB_HEAP_ID = 10,
ION_VPU_HEAP_ID = 11,
ION_JPU_HEAP_ID = 12,
HISI_ION_HEAP_IOMMU_ID = 13,
ION_MISC_HEAP_ID = 14,
ION_DRM_GRALLOC_HEAP_ID=15,
ION_DRM_VCODEC_HEAP_ID =16,
ION_TUI_HEAP_ID=17,
ION_IRIS_HEAP_ID=18,
ION_RESERV2_ID=19,
ION_DRM_HEAP_ID=20,
ION_HEAP_ID_RESERVED = 31, /* Bit reserved */
};
/**
* Macro should be used with ion_heap_ids defined above.
*/
#define ION_HEAP(bit) (1 << (bit))
#define ION_8K_ALIGN(len) ALIGN(len, SZ_8K)
#define IOMMU_PAGE_SIZE SZ_8K
#define ION_VMALLOC_HEAP_NAME "vmalloc"
#define ION_KMALLOC_HEAP_NAME "kmalloc"
#define ION_GRALLOC_HEAP_NAME "gralloc"
#define ION_SET_CACHED(__cache) (__cache | ION_FLAG_CACHED)
#define ION_SET_UNCACHED(__cache) (__cache & ~ION_FLAG_CACHED)
#define ION_IS_CACHED(__flags) ((__flags) & ION_FLAG_CACHED)
//struct used for get phys addr of contig heap
struct ion_phys_data {
int fd_buffer;
unsigned int size;
union {
unsigned int phys;
unsigned int phys_l;
};
unsigned int phys_h;
};
struct ion_flag_data {
int shared_fd;
int flags;
};
struct ion_smart_pool_info_data {
int water_mark;
};
#define HISI_ION_NAME_LEN 16
struct ion_heap_info_data{
char name[HISI_ION_NAME_LEN];
phys_addr_t heap_phy;
unsigned int heap_size;
};
struct ion_kern_va_data {
int handle_id;
unsigned int kern_va_h;
unsigned int kern_va_l;
};
struct ion_issupport_iommu_data{
int is_support_iommu;
};
struct ion_flush_data {
int fd;
void *vaddr;
unsigned int offset;
unsigned int length;
};
//user command add for additional use
enum ION_HISI_CUSTOM_CMD {
ION_HISI_CUSTOM_PHYS,
ION_HISI_CLEAN_CACHES,
ION_HISI_INV_CACHES,
ION_HISI_CLEAN_INV_CACHES,
ION_HISI_CUSTOM_GET_KERN_VA,
ION_HISI_CUSTOM_FREE_KERN_VA,
ION_HISI_CUSTOM_ISSUPPORT_IOMMU,
ION_HISI_CUSTOM_GET_MEDIA_HEAP_MODE,
ION_HISI_CUSTOM_SET_FLAG,
ION_HISI_CUSTOM_SET_SMART_POOL_INFO,
};
enum ION_HISI_HEAP_MODE {
ION_CARVEROUT_MODE=0,
ION_IOMMU_MODE=1,
};
#define TINY_SYSTEM 0x0 /* tiny version system for chip test*/
#define FULL_SYSTEM 0x1 /* full version system */
/**
* hisi_ion_client_create() - create iommu mapping for the given handle
* @heap_mask: ion heap type mask
* @name: the client name
* @return: the client handle
*
* This function should called by high-level user in kernel. Before users
* can access a buffer, they should get a client via calling this function.
*/
struct ion_client *
hisi_ion_client_create(const char *name);
int hisi_ion_get_heap_info(unsigned int id,struct ion_heap_info_data* data);
int hisi_ion_get_media_mode(void);
unsigned long long get_system_type(void);
struct ion_device * get_ion_device(void);
#define ION_IOC_HISI_MAGIC 'H'
/**
*DOC: ION_IOC_FLUSH_ALL_CACHES - flush all the caches pf L1 and L2
*
*flush all the caches pf L1 and L2
*/
#define ION_IOC_FLUSH_ALL_CACHES _IOWR(ION_IOC_HISI_MAGIC, 3, \
struct ion_flush_data)
#ifdef CONFIG_ION
extern unsigned long hisi_ion_total(void);
#else
static inline unsigned long hisi_ion_total(void)
{
return 0;
}
#endif
/*k3 add to calc free memory*/
void hisi_ionsysinfo(struct sysinfo *si);
int ion_handle_get_flags(struct ion_client *client, struct ion_handle *handle,
unsigned long *flags);
int hisi_ion_memory_info(bool verbose);
#endif
+79
View File
@@ -0,0 +1,79 @@
#ifndef _HISI_IOMMU_H_
#define _HISI_IOMMU_H_
#include <linux/list.h>
#include <linux/iommu.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#ifdef CONFIG_HISI_IODOMAIN_API
struct section_info {
unsigned int iova_start;
unsigned int iova_size;
unsigned int page_size;
unsigned int align;
};
struct hisi_iommu_domain {
struct iommu_domain *domain;
struct gen_pool *iova_pool;
struct section_info range;
};
/**
* hisi iommu domain interface
*/
struct hisi_iommu_domain * hisi_get_domain(void);
size_t hisi_iommu_iova_size(void);
size_t hisi_iommu_iova_available(void);
void hisi_iommu_free_iova(unsigned long iova, size_t size);
unsigned long hisi_iommu_alloc_iova(size_t size, unsigned long align);
int hisi_iommu_map_range(struct iommu_domain *domain,unsigned long iova_start, struct scatterlist *sgl,
unsigned long iova_size,unsigned int prot);
int hisi_iommu_unmap_range(struct iommu_domain *domain,unsigned long iova_start,unsigned long iova_size);
int hisi_iommu_map_domain(struct scatterlist *sg,struct iommu_map_format *format);
int hisi_iommu_unmap_domain(struct iommu_map_format *format);
phys_addr_t hisi_iommu_domain_iova_to_phys(unsigned long iova);
unsigned int hisi_iommu_page_size (void);
bool hisi_iommu_off_on(void);
int hisi_iommu_get_info(unsigned int *iova_start, unsigned int *pgtbl_base);
#else
/**
* hisi iommu domain interface
*/
static inline int hisi_iommu_map_domain(struct scatterlist *sg,
struct iommu_map_format *format)
{
return 0;
}
static inline int hisi_iommu_unmap_domain(struct iommu_map_format *format)
{
return 0;
}
static inline phys_addr_t hisi_iommu_domain_iova_to_phys(unsigned long iova)
{
return 0;
}
unsigned int hisi_iommu_page_size (void)
{
return SZ_4K;
}
bool hisi_iommu_off_on(void)
{
return false;
}
static inline int hisi_iommu_get_info(unsigned int *iova_start, unsigned int *pgtbl_base)
{
return 0;
}
#endif /* CONFIG_HISI_IODOMAIN_API */
#endif /* _HISI_IOMMU_H_ */
+178
View File
@@ -0,0 +1,178 @@
/*
*
* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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.
*
*/
#ifndef _LINUX_HISI_ION_H
#define _LINUX_HISI_ION_H
#include <linux/ion.h>
#include <linux/sizes.h>
/**
* These are the only ids that should be used for Ion heap ids.
* The ids listed are the order in which allocation will be attempted
* if specified. Don't swap the order of heap ids unless you know what
* you are doing!
* Id's are spaced by purpose to allow new Id's to be inserted in-between (for
* possible fallbacks)
*/
enum ion_heap_ids {
INVALID_HEAP_ID = -1,
ION_SYSTEM_HEAP_ID = 0,
ION_SYSTEM_CONTIG_HEAP_ID = 1,
ION_GRALLOC_HEAP_ID = 2,
ION_DMA_HEAP_ID = 3,
ION_DMA_POOL_HEAP_ID = 4,
ION_CPU_DRAW_HEAP_ID = 5,
ION_CAMERA_HEAP_ID = 6,
ION_OVERLAY_HEAP_ID = 7,
ION_VCODEC_HEAP_ID = 8,
ION_ISP_HEAP_ID = 9,
ION_FB_HEAP_ID = 10,
ION_VPU_HEAP_ID = 11,
ION_JPU_HEAP_ID = 12,
HISI_ION_HEAP_IOMMU_ID = 13,
ION_MISC_HEAP_ID = 14,
ION_DRM_GRALLOC_HEAP_ID=15,
ION_DRM_VCODEC_HEAP_ID =16,
ION_TUI_HEAP_ID=17,
ION_IRIS_HEAP_ID=18,
ION_RESERV2_ID=19,
ION_DRM_HEAP_ID=20,
ION_HEAP_ID_RESERVED = 31, /* Bit reserved */
};
/**
* Macro should be used with ion_heap_ids defined above.
*/
#define ION_HEAP(bit) (1 << (bit))
#define ION_8K_ALIGN(len) ALIGN(len, SZ_8K)
#define IOMMU_PAGE_SIZE SZ_8K
#define ION_VMALLOC_HEAP_NAME "vmalloc"
#define ION_KMALLOC_HEAP_NAME "kmalloc"
#define ION_GRALLOC_HEAP_NAME "gralloc"
#define ION_SET_CACHED(__cache) (__cache | ION_FLAG_CACHED)
#define ION_SET_UNCACHED(__cache) (__cache & ~ION_FLAG_CACHED)
#define ION_IS_CACHED(__flags) ((__flags) & ION_FLAG_CACHED)
//struct used for get phys addr of contig heap
struct ion_phys_data {
int fd_buffer;
unsigned int size;
union {
unsigned int phys;
unsigned int phys_l;
};
unsigned int phys_h;
};
struct ion_flag_data {
int shared_fd;
int flags;
};
struct ion_smart_pool_info_data {
int water_mark;
};
#define HISI_ION_NAME_LEN 16
struct ion_heap_info_data{
char name[HISI_ION_NAME_LEN];
phys_addr_t heap_phy;
unsigned int heap_size;
};
struct ion_kern_va_data {
int handle_id;
unsigned int kern_va_h;
unsigned int kern_va_l;
};
struct ion_issupport_iommu_data{
int is_support_iommu;
};
struct ion_flush_data {
int fd;
void *vaddr;
unsigned int offset;
unsigned int length;
};
//user command add for additional use
enum ION_HISI_CUSTOM_CMD {
ION_HISI_CUSTOM_PHYS,
ION_HISI_CLEAN_CACHES,
ION_HISI_INV_CACHES,
ION_HISI_CLEAN_INV_CACHES,
ION_HISI_CUSTOM_GET_KERN_VA,
ION_HISI_CUSTOM_FREE_KERN_VA,
ION_HISI_CUSTOM_ISSUPPORT_IOMMU,
ION_HISI_CUSTOM_GET_MEDIA_HEAP_MODE,
ION_HISI_CUSTOM_SET_FLAG,
ION_HISI_CUSTOM_SET_SMART_POOL_INFO,
};
enum ION_HISI_HEAP_MODE {
ION_CARVEROUT_MODE=0,
ION_IOMMU_MODE=1,
};
#define TINY_SYSTEM 0x0 /* tiny version system for chip test*/
#define FULL_SYSTEM 0x1 /* full version system */
/**
* hisi_ion_client_create() - create iommu mapping for the given handle
* @heap_mask: ion heap type mask
* @name: the client name
* @return: the client handle
*
* This function should called by high-level user in kernel. Before users
* can access a buffer, they should get a client via calling this function.
*/
struct ion_client *
hisi_ion_client_create(const char *name);
int hisi_ion_get_heap_info(unsigned int id,struct ion_heap_info_data* data);
int hisi_ion_get_media_mode(void);
unsigned long long get_system_type(void);
struct ion_device * get_ion_device(void);
#define ION_IOC_HISI_MAGIC 'H'
/**
*DOC: ION_IOC_FLUSH_ALL_CACHES - flush all the caches pf L1 and L2
*
*flush all the caches pf L1 and L2
*/
#define ION_IOC_FLUSH_ALL_CACHES _IOWR(ION_IOC_HISI_MAGIC, 3, \
struct ion_flush_data)
#ifdef CONFIG_ION
extern unsigned long hisi_ion_total(void);
#else
static inline unsigned long hisi_ion_total(void)
{
return 0;
}
#endif
/*k3 add to calc free memory*/
void hisi_ionsysinfo(struct sysinfo *si);
int ion_handle_get_flags(struct ion_client *client, struct ion_handle *handle,
unsigned long *flags);
int hisi_ion_memory_info(bool verbose);
#endif
+43
View File
@@ -31,6 +31,11 @@
#define IOMMU_CACHE (1 << 2) /* DMA cache coherency */
#define IOMMU_NOEXEC (1 << 3)
#define IOMMU_MMIO (1 << 4) /* e.g. things like MSI doorbells */
#if CONFIG_HISI_IOMMU
#define IOMMU_DEVICE (1 << 4)
#define IOMMU_SEC (1 << 5)
#define IOMMU_EXEC (1 << 6)
#endif
struct iommu_ops;
struct iommu_group;
@@ -84,6 +89,7 @@ struct iommu_domain {
void *handler_token;
struct iommu_domain_geometry geometry;
void *iova_cookie;
void *priv;
};
enum iommu_cap {
@@ -117,6 +123,28 @@ enum iommu_attr {
DOMAIN_ATTR_MAX,
};
#if CONFIG_HISI_IOMMU
/* metadata for iommu mapping */
struct iommu_map_format {
unsigned long iova_start;
unsigned long iova_size;
unsigned long iommu_ptb_base;
unsigned long iommu_iova_base;
unsigned long header_size;
unsigned long phys_page_line;
unsigned long virt_page_line;
unsigned long is_tile;
unsigned long prot;
};
struct tile_format {
unsigned long header_size;
unsigned long is_tile;
unsigned long phys_page_line;
unsigned long virt_page_line;
};
#endif
/**
* struct iommu_dm_region - descriptor for a direct mapped memory region
* @list: Linked list pointers
@@ -201,6 +229,14 @@ struct iommu_ops {
int (*of_xlate)(struct device *dev, struct of_phandle_args *args);
#if CONFIG_HISI_IOMMU
int (*map_tile)(struct iommu_domain *domain, unsigned long iova,
struct scatterlist *sg, size_t size, int prot,
struct tile_format *format);
size_t (*unmap_tile)(struct iommu_domain *domain, unsigned long iova,
size_t size);
#endif
unsigned long pgsize_bitmap;
};
@@ -272,7 +308,14 @@ struct device *iommu_device_create(struct device *parent, void *drvdata,
void iommu_device_destroy(struct device *dev);
int iommu_device_link(struct device *dev, struct device *link);
void iommu_device_unlink(struct device *dev, struct device *link);
#if CONFIG_HISI_IOMMU
int iommu_map_tile(struct iommu_domain *domain, unsigned long iova,
struct scatterlist *sg, size_t size, int prot,
struct tile_format *format);
int iommu_unmap_tile(struct iommu_domain *domain, unsigned long iova,
size_t size);
#endif
/* Window handling function prototypes */
extern int iommu_domain_window_enable(struct iommu_domain *domain, u32 wnd_nr,
phys_addr_t offset, u64 size,
+5
View File
@@ -0,0 +1,5 @@
#ifndef _INCLUDE_LINUX_ION_H_
#define _INCLUDE_LINUX_ION_H_
#include "../../drivers/staging/android/uapi/ion.h"
#include "../../drivers/staging/android/ion/ion.h"
#endif /* _INCLUDE_LINUX_ION_H_ */