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:
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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");
|
||||
@@ -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.
|
||||
@@ -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
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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_ */
|
||||
@@ -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
|
||||
@@ -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,
|
||||
|
||||
@@ -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_ */
|
||||
Reference in New Issue
Block a user