drivers:iommu: muti-domain for smmu on hikey970 platform
On the hikey960 platform, only one domain is supported, and you can see that the media business is all used by the domain created by the ion. After adding IPU (AI) to Hikey970 platform, it uses its own domain, so the underlying layer needs to support multiple page mapping of domain, otherwise IPU and DRM mapping will conflict, resulting in IPU recognition of image failure. Signed-off-by: Li Wei <liwei213@huawei.com>
This commit is contained in:
@@ -678,14 +678,12 @@ static int dss_enable_iommu(struct platform_device *pdev, struct dss_hw_ctx *ctx
|
|||||||
dev = &pdev->dev;
|
dev = &pdev->dev;
|
||||||
|
|
||||||
/* create iommu domain */
|
/* create iommu domain */
|
||||||
ctx->mmu_domain = iommu_domain_alloc(dev->bus);
|
ctx->mmu_domain = hisi_ion_enable_iommu(NULL);
|
||||||
if (!ctx->mmu_domain) {
|
if (!ctx->mmu_domain) {
|
||||||
pr_err("iommu_domain_alloc failed!\n");
|
pr_err("iommu_domain_alloc failed!\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
iommu_attach_device(ctx->mmu_domain, dev);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#ifndef HISI_SMMU_H
|
#ifndef HISI_SMMU_H
|
||||||
#define HISI_SMMU_H
|
#define HISI_SMMU_H
|
||||||
|
#include <linux/iommu.h>
|
||||||
|
|
||||||
/*#define IOMMU_DEBUG*/
|
/*#define IOMMU_DEBUG*/
|
||||||
#ifdef IOMMU_DEBUG
|
#ifdef IOMMU_DEBUG
|
||||||
@@ -80,15 +81,19 @@ typedef u64 smmu_pgd_t;
|
|||||||
typedef u64 smmu_pmd_t;
|
typedef u64 smmu_pmd_t;
|
||||||
typedef u64 smmu_pte_t;
|
typedef u64 smmu_pte_t;
|
||||||
|
|
||||||
|
struct hisi_domain {
|
||||||
|
spinlock_t lock;
|
||||||
|
struct list_head list;
|
||||||
|
smmu_pgd_t *va_pgtable_addr;
|
||||||
|
smmu_pgd_t *va_pgtable_addr_orig;
|
||||||
|
phys_addr_t pa_pgtable_addr;
|
||||||
|
struct iommu_domain domain;
|
||||||
|
};
|
||||||
|
|
||||||
/*smmu device object*/
|
/*smmu device object*/
|
||||||
struct hisi_smmu_device_lpae {
|
struct hisi_smmu_device_lpae {
|
||||||
struct device *dev ;
|
struct device *dev ;
|
||||||
struct list_head domain_list;
|
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 hisi_map_tile_position_lpae {
|
||||||
|
|||||||
+103
-124
@@ -31,6 +31,11 @@
|
|||||||
|
|
||||||
struct hisi_smmu_device_lpae *hisi_smmu_dev;
|
struct hisi_smmu_device_lpae *hisi_smmu_dev;
|
||||||
|
|
||||||
|
static struct hisi_domain *to_hisi_domain(struct iommu_domain *dom)
|
||||||
|
{
|
||||||
|
return container_of(dom, struct hisi_domain, domain);
|
||||||
|
}
|
||||||
|
|
||||||
/*transfer 64bit pte table pointer to struct page*/
|
/*transfer 64bit pte table pointer to struct page*/
|
||||||
static pgtable_t smmu_pgd_to_pte_lpae(unsigned int ppte_table)
|
static pgtable_t smmu_pgd_to_pte_lpae(unsigned int ppte_table)
|
||||||
{
|
{
|
||||||
@@ -57,41 +62,40 @@ static pgtable_t smmu_pmd_to_pte_lpae(unsigned long ppte_table)
|
|||||||
return table;
|
return table;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int get_domain_data_lpae(struct device_node *np,
|
int of_get_iova_info(struct device_node *np, unsigned long *iova_start,
|
||||||
struct iommu_domain_data *data)
|
unsigned long *iova_size, unsigned long *iova_align)
|
||||||
{
|
{
|
||||||
unsigned long long align;
|
struct device_node *node;
|
||||||
struct device_node *node = NULL;
|
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
data->phy_pgd_base = hisi_smmu_dev->smmu_phy_pgtable_addr;
|
if (!np)
|
||||||
if (np) {
|
return -ENODEV;
|
||||||
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",
|
node = of_find_node_by_name(np, "iommu_info");
|
||||||
__func__, data->iova_start,
|
if (!node) {
|
||||||
data->iova_size, data->iova_align);
|
pr_err("find iommu_info node error\n");
|
||||||
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
ret = of_property_read_u32(node, "start-addr",
|
||||||
|
(u32 *)iova_start);
|
||||||
|
if (ret) {
|
||||||
|
pr_err("read iova start address error\n");
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto read_error;
|
||||||
|
}
|
||||||
|
ret = of_property_read_u32(node, "size", (u32 *)iova_size);
|
||||||
|
if (ret) {
|
||||||
|
pr_err("read iova size error\n");
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto read_error;
|
||||||
|
}
|
||||||
|
ret = of_property_read_u64(node, "iova-align", (u64 *)iova_align);
|
||||||
|
if (ret)
|
||||||
|
*iova_align = SZ_256K;
|
||||||
|
|
||||||
|
pr_err("%s:start_addr 0x%lx, size 0x%lx align 0x%lx\n",
|
||||||
|
__func__, *iova_start, *iova_size,
|
||||||
|
*iova_align);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@@ -102,18 +106,32 @@ read_error:
|
|||||||
static struct iommu_domain
|
static struct iommu_domain
|
||||||
*hisi_smmu_domain_alloc_lpae(unsigned iommu_domain_type)
|
*hisi_smmu_domain_alloc_lpae(unsigned iommu_domain_type)
|
||||||
{
|
{
|
||||||
struct iommu_domain *domain;
|
struct hisi_domain *hisi_domain;
|
||||||
|
|
||||||
if (iommu_domain_type != IOMMU_DOMAIN_UNMANAGED)
|
if (iommu_domain_type != IOMMU_DOMAIN_UNMANAGED)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
domain = kzalloc(sizeof(*domain), GFP_KERNEL);
|
hisi_domain = kzalloc(sizeof(*hisi_domain), GFP_KERNEL);
|
||||||
if (!domain) {
|
if (!hisi_domain)
|
||||||
pr_err("%s: fail to kzalloc %lu bytes\n",
|
return NULL;
|
||||||
__func__, sizeof(*domain));
|
|
||||||
}
|
|
||||||
|
|
||||||
return domain;
|
hisi_domain->va_pgtable_addr_orig = (smmu_pgd_t *)kzalloc(SZ_64, GFP_KERNEL | __GFP_DMA);
|
||||||
|
if (!hisi_domain->va_pgtable_addr_orig)
|
||||||
|
goto err_smmu_pgd;
|
||||||
|
|
||||||
|
hisi_domain->va_pgtable_addr = (smmu_pgd_t *)(ALIGN((unsigned long)
|
||||||
|
(hisi_domain->va_pgtable_addr_orig), SZ_32));
|
||||||
|
|
||||||
|
hisi_domain->pa_pgtable_addr = virt_to_phys(hisi_domain->va_pgtable_addr);
|
||||||
|
spin_lock_init(&hisi_domain->lock);
|
||||||
|
|
||||||
|
list_add(&hisi_domain->list, &hisi_smmu_dev->domain_list);
|
||||||
|
|
||||||
|
return &hisi_domain->domain;
|
||||||
|
|
||||||
|
err_smmu_pgd:
|
||||||
|
kfree(hisi_domain);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -147,17 +165,19 @@ static void hisi_smmu_free_pmds_lpae(smmu_pgd_t pgd)
|
|||||||
smmu_set_pgd_lpae(&pgd, 0);
|
smmu_set_pgd_lpae(&pgd, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hisi_smmu_free_pgtables_lpae(unsigned long *page_table_addr)
|
static void hisi_smmu_free_pgtables_lpae(struct iommu_domain *domain,
|
||||||
|
unsigned long *page_table_addr)
|
||||||
{
|
{
|
||||||
int i, j;
|
int i, j;
|
||||||
smmu_pgd_t *pgd;
|
smmu_pgd_t *pgd;
|
||||||
smmu_pmd_t *pmd;
|
smmu_pmd_t *pmd;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
struct hisi_domain *hisi_domain = to_hisi_domain(domain);
|
||||||
|
|
||||||
pgd = (smmu_pgd_t *)page_table_addr;
|
pgd = (smmu_pgd_t *)page_table_addr;
|
||||||
pmd = (smmu_pmd_t *)page_table_addr;
|
pmd = (smmu_pmd_t *)page_table_addr;
|
||||||
|
|
||||||
spin_lock_irqsave(&hisi_smmu_dev->lock, flags);
|
spin_lock_irqsave(&hisi_domain->lock, flags);
|
||||||
for (i = 0; i < SMMU_PTRS_PER_PGD; ++i) {
|
for (i = 0; i < SMMU_PTRS_PER_PGD; ++i) {
|
||||||
if ((smmu_pgd_none_lpae(*pgd)) & (smmu_pmd_none_lpae(*pmd)))
|
if ((smmu_pgd_none_lpae(*pgd)) & (smmu_pmd_none_lpae(*pmd)))
|
||||||
continue;
|
continue;
|
||||||
@@ -169,38 +189,39 @@ static void hisi_smmu_free_pgtables_lpae(unsigned long *page_table_addr)
|
|||||||
pgd++;
|
pgd++;
|
||||||
}
|
}
|
||||||
memset((void *)page_table_addr, 0, PAGE_SIZE);
|
memset((void *)page_table_addr, 0, PAGE_SIZE);
|
||||||
spin_unlock_irqrestore(&hisi_smmu_dev->lock, flags);
|
spin_unlock_irqrestore(&hisi_domain->lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hisi_smmu_domain_free_lpae(struct iommu_domain *domain)
|
static void hisi_smmu_domain_free_lpae(struct iommu_domain *domain)
|
||||||
{
|
{
|
||||||
if (list_empty(&hisi_smmu_dev->domain_list))
|
struct hisi_domain *hisi_domain = to_hisi_domain(domain);
|
||||||
hisi_smmu_free_pgtables_lpae((unsigned long *)
|
hisi_smmu_free_pgtables_lpae(domain, (unsigned long *)
|
||||||
hisi_smmu_dev->va_pgtable_addr);
|
hisi_domain->va_pgtable_addr);
|
||||||
|
|
||||||
kfree(domain);
|
|
||||||
|
|
||||||
|
list_del(&hisi_domain->list);
|
||||||
|
kfree(hisi_domain->va_pgtable_addr_orig);
|
||||||
|
kfree(hisi_domain);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int hisi_smmu_alloc_init_pte_lpae(smmu_pmd_t *ppmd,
|
static int hisi_smmu_alloc_init_pte_lpae(struct iommu_domain *domain,
|
||||||
unsigned long addr, unsigned long end,
|
smmu_pmd_t *ppmd, unsigned long addr, unsigned long end,
|
||||||
unsigned long pfn, u64 prot, unsigned long *flags)
|
unsigned long pfn, u64 prot, unsigned long *flags)
|
||||||
{
|
{
|
||||||
smmu_pte_t *pte, *start;
|
smmu_pte_t *pte, *start;
|
||||||
pgtable_t table;
|
pgtable_t table;
|
||||||
u64 pteval = SMMU_PTE_TYPE;
|
u64 pteval = SMMU_PTE_TYPE;
|
||||||
|
struct hisi_domain *hisi_domain = to_hisi_domain(domain);
|
||||||
|
|
||||||
if (!smmu_pmd_none_lpae(*ppmd))
|
if (!smmu_pmd_none_lpae(*ppmd))
|
||||||
goto pte_ready;
|
goto pte_ready;
|
||||||
|
|
||||||
/* Allocate a new set of tables */
|
/* Allocate a new set of tables */
|
||||||
spin_unlock_irqrestore(&hisi_smmu_dev->lock, *flags);
|
|
||||||
table = alloc_page(GFP_KERNEL | __GFP_ZERO | __GFP_DMA);
|
table = alloc_page(GFP_KERNEL | __GFP_ZERO | __GFP_DMA);
|
||||||
spin_lock_irqsave(&hisi_smmu_dev->lock, *flags);
|
|
||||||
if (!table) {
|
if (!table) {
|
||||||
dbg("%s: alloc page fail\n", __func__);
|
dbg("%s: alloc page fail\n", __func__);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
spin_lock_irqsave(&hisi_domain->lock, *flags);
|
||||||
|
|
||||||
if (smmu_pmd_none_lpae(*ppmd)) {
|
if (smmu_pmd_none_lpae(*ppmd)) {
|
||||||
hisi_smmu_flush_pgtable_lpae(page_address(table),
|
hisi_smmu_flush_pgtable_lpae(page_address(table),
|
||||||
@@ -209,6 +230,7 @@ static int hisi_smmu_alloc_init_pte_lpae(smmu_pmd_t *ppmd,
|
|||||||
hisi_smmu_flush_pgtable_lpae(ppmd, sizeof(*ppmd));
|
hisi_smmu_flush_pgtable_lpae(ppmd, sizeof(*ppmd));
|
||||||
} else
|
} else
|
||||||
__free_page(table);
|
__free_page(table);
|
||||||
|
spin_unlock_irqrestore(&hisi_domain->lock, *flags);
|
||||||
|
|
||||||
pte_ready:
|
pte_ready:
|
||||||
if (prot & IOMMU_SEC)
|
if (prot & IOMMU_SEC)
|
||||||
@@ -261,27 +283,26 @@ pte_ready:
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int hisi_smmu_alloc_init_pmd_lpae(smmu_pgd_t *ppgd,
|
static int hisi_smmu_alloc_init_pmd_lpae(struct iommu_domain *domain,
|
||||||
unsigned long addr, unsigned long end,
|
smmu_pgd_t *ppgd, unsigned long addr, unsigned long end,
|
||||||
unsigned long paddr, int prot, unsigned long *flags)
|
unsigned long paddr, int prot, unsigned long *flags)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
smmu_pmd_t *ppmd, *start;
|
smmu_pmd_t *ppmd, *start;
|
||||||
u64 next;
|
u64 next;
|
||||||
pgtable_t table;
|
pgtable_t table;
|
||||||
|
struct hisi_domain *hisi_domain = to_hisi_domain(domain);
|
||||||
|
|
||||||
if (!smmu_pgd_none_lpae(*ppgd))
|
if (!smmu_pgd_none_lpae(*ppgd))
|
||||||
goto pmd_ready;
|
goto pmd_ready;
|
||||||
|
|
||||||
/* Allocate a new set of tables */
|
/* Allocate a new set of tables */
|
||||||
spin_unlock_irqrestore(&hisi_smmu_dev->lock, *flags);
|
|
||||||
table = alloc_page(GFP_KERNEL | __GFP_ZERO | __GFP_DMA);
|
table = alloc_page(GFP_KERNEL | __GFP_ZERO | __GFP_DMA);
|
||||||
spin_lock_irqsave(&hisi_smmu_dev->lock, *flags);
|
|
||||||
if (!table) {
|
if (!table) {
|
||||||
dbg("%s: alloc page fail\n", __func__);
|
dbg("%s: alloc page fail\n", __func__);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
spin_lock_irqsave(&hisi_domain->lock, *flags);
|
||||||
if (smmu_pgd_none_lpae(*ppgd)) {
|
if (smmu_pgd_none_lpae(*ppgd)) {
|
||||||
hisi_smmu_flush_pgtable_lpae(page_address(table),
|
hisi_smmu_flush_pgtable_lpae(page_address(table),
|
||||||
SMMU_PAGE_SIZE);
|
SMMU_PAGE_SIZE);
|
||||||
@@ -289,6 +310,7 @@ static int hisi_smmu_alloc_init_pmd_lpae(smmu_pgd_t *ppgd,
|
|||||||
hisi_smmu_flush_pgtable_lpae(ppgd, sizeof(*ppgd));
|
hisi_smmu_flush_pgtable_lpae(ppgd, sizeof(*ppgd));
|
||||||
} else
|
} else
|
||||||
__free_page(table);
|
__free_page(table);
|
||||||
|
spin_unlock_irqrestore(&hisi_domain->lock, *flags);
|
||||||
|
|
||||||
pmd_ready:
|
pmd_ready:
|
||||||
if (prot & IOMMU_SEC)
|
if (prot & IOMMU_SEC)
|
||||||
@@ -299,7 +321,7 @@ pmd_ready:
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
next = smmu_pmd_addr_end_lpae(addr, end);
|
next = smmu_pmd_addr_end_lpae(addr, end);
|
||||||
ret = hisi_smmu_alloc_init_pte_lpae(ppmd,
|
ret = hisi_smmu_alloc_init_pte_lpae(domain, ppmd,
|
||||||
addr, next, __phys_to_pfn(paddr), prot, flags);
|
addr, next, __phys_to_pfn(paddr), prot, flags);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto error;
|
goto error;
|
||||||
@@ -318,7 +340,9 @@ int hisi_smmu_handle_mapping_lpae(struct iommu_domain *domain,
|
|||||||
unsigned long end;
|
unsigned long end;
|
||||||
unsigned long next;
|
unsigned long next;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
smmu_pgd_t *pgd = (smmu_pgd_t *)hisi_smmu_dev->va_pgtable_addr;
|
|
||||||
|
struct hisi_domain *hisi_domain = to_hisi_domain(domain);
|
||||||
|
smmu_pgd_t *pgd = (smmu_pgd_t *)hisi_domain->va_pgtable_addr;
|
||||||
|
|
||||||
if (!pgd) {
|
if (!pgd) {
|
||||||
dbg("pgd is null\n");
|
dbg("pgd is null\n");
|
||||||
@@ -326,12 +350,11 @@ int hisi_smmu_handle_mapping_lpae(struct iommu_domain *domain,
|
|||||||
}
|
}
|
||||||
iova = ALIGN(iova, SMMU_PAGE_SIZE);
|
iova = ALIGN(iova, SMMU_PAGE_SIZE);
|
||||||
size = ALIGN(size, SMMU_PAGE_SIZE);
|
size = ALIGN(size, SMMU_PAGE_SIZE);
|
||||||
spin_lock_irqsave(&hisi_smmu_dev->lock, flags);
|
|
||||||
pgd += smmu_pgd_index(iova);
|
pgd += smmu_pgd_index(iova);
|
||||||
end = iova + size;
|
end = iova + size;
|
||||||
do {
|
do {
|
||||||
next = smmu_pgd_addr_end_lpae(iova, end);
|
next = smmu_pgd_addr_end_lpae(iova, end);
|
||||||
ret = hisi_smmu_alloc_init_pmd_lpae(pgd,
|
ret = hisi_smmu_alloc_init_pmd_lpae(domain, pgd,
|
||||||
iova, next, paddr, prot, &flags);
|
iova, next, paddr, prot, &flags);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
@@ -339,7 +362,6 @@ int hisi_smmu_handle_mapping_lpae(struct iommu_domain *domain,
|
|||||||
iova = next;
|
iova = next;
|
||||||
} while (pgd++, iova < end);
|
} while (pgd++, iova < end);
|
||||||
out_unlock:
|
out_unlock:
|
||||||
spin_unlock_irqrestore(&hisi_smmu_dev->lock, flags);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -348,29 +370,15 @@ static int hisi_smmu_map_lpae(struct iommu_domain *domain,
|
|||||||
phys_addr_t paddr, size_t size,
|
phys_addr_t paddr, size_t size,
|
||||||
int prot)
|
int prot)
|
||||||
{
|
{
|
||||||
unsigned long max_iova;
|
|
||||||
struct iommu_domain_data *data;
|
struct iommu_domain_data *data;
|
||||||
|
|
||||||
if (!domain) {
|
if (!domain) {
|
||||||
dbg("domain is null\n");
|
dbg("domain is null\n");
|
||||||
|
data = NULL;
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
data = domain->priv;
|
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);
|
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,
|
static unsigned int hisi_smmu_clear_pte_lpae(smmu_pgd_t *pmdp,
|
||||||
@@ -416,59 +424,42 @@ unsigned int hisi_smmu_handle_unmapping_lpae(struct iommu_domain *domain,
|
|||||||
unsigned int end = 0;
|
unsigned int end = 0;
|
||||||
unsigned int next = 0;
|
unsigned int next = 0;
|
||||||
unsigned int unmap_size = 0;
|
unsigned int unmap_size = 0;
|
||||||
unsigned long flags;
|
struct hisi_domain *hisi_domain = to_hisi_domain(domain);
|
||||||
|
|
||||||
iova = SMMU_PAGE_ALIGN(iova);
|
iova = SMMU_PAGE_ALIGN(iova);
|
||||||
size = SMMU_PAGE_ALIGN(size);
|
size = SMMU_PAGE_ALIGN(size);
|
||||||
pgdp = (smmu_pgd_t *)hisi_smmu_dev->va_pgtable_addr;
|
pgdp = (smmu_pgd_t *)hisi_domain->va_pgtable_addr;
|
||||||
end = iova + size;
|
end = iova + size;
|
||||||
dbg("%s:end=0x%x\n", __func__, end);
|
|
||||||
pgdp += smmu_pgd_index(iova);
|
pgdp += smmu_pgd_index(iova);
|
||||||
spin_lock_irqsave(&hisi_smmu_dev->lock, flags);
|
|
||||||
do {
|
do {
|
||||||
next = smmu_pgd_addr_end_lpae(iova, end);
|
next = smmu_pgd_addr_end_lpae(iova, end);
|
||||||
unmap_size += hisi_smmu_clear_pmd_lpae(pgdp, iova, next);
|
unmap_size += hisi_smmu_clear_pmd_lpae(pgdp, iova, next);
|
||||||
iova = next;
|
iova = next;
|
||||||
dbg("%s: pgdp=%p, iova=0x%lx\n", __func__, pgdp, iova);
|
dbg("%s: pgdp=%pK, iova=0x%lx\n", __func__, pgdp, iova);
|
||||||
} while (pgdp++, iova < end);
|
} while (pgdp++, iova < end);
|
||||||
|
|
||||||
spin_unlock_irqrestore(&hisi_smmu_dev->lock, flags);
|
|
||||||
return unmap_size;
|
return unmap_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t hisi_smmu_unmap_lpae(struct iommu_domain *domain,
|
static size_t hisi_smmu_unmap_lpae(struct iommu_domain *domain,
|
||||||
unsigned long iova, size_t size)
|
unsigned long iova, size_t size)
|
||||||
{
|
{
|
||||||
unsigned long max_iova;
|
|
||||||
unsigned int ret;
|
unsigned int ret;
|
||||||
struct iommu_domain_data *data;
|
struct iommu_domain_data *data;
|
||||||
|
|
||||||
if (!domain) {
|
if (!domain) {
|
||||||
dbg("domain is null\n");
|
dbg("domain is null\n");
|
||||||
return -ENODEV;
|
return 0;
|
||||||
}
|
}
|
||||||
data = domain->priv;
|
data = domain->priv;
|
||||||
/*caculate the max io virtual address */
|
/*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*/
|
/*unmapping the range of iova*/
|
||||||
ret = hisi_smmu_handle_unmapping_lpae(domain, iova, size);
|
ret = hisi_smmu_handle_unmapping_lpae(domain, iova, size);
|
||||||
if (ret == size) {
|
if (ret == size) {
|
||||||
dbg("%s:unmap size:0x%x\n", __func__, (unsigned int)size);
|
dbg("%s:unmap size:0x%x\n", __func__, (unsigned int)size);
|
||||||
return size;
|
return size;
|
||||||
} else {
|
} else
|
||||||
return 0;
|
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(
|
static phys_addr_t hisi_smmu_iova_to_phys_lpae(
|
||||||
@@ -478,7 +469,8 @@ static phys_addr_t hisi_smmu_iova_to_phys_lpae(
|
|||||||
smmu_pmd_t pmd;
|
smmu_pmd_t pmd;
|
||||||
smmu_pte_t pte;
|
smmu_pte_t pte;
|
||||||
|
|
||||||
pgdp = (smmu_pgd_t *)hisi_smmu_dev->va_pgtable_addr;
|
struct hisi_domain *hisi_domain = to_hisi_domain(domain);
|
||||||
|
pgdp = (smmu_pgd_t *)hisi_domain->va_pgtable_addr;
|
||||||
if (!pgdp)
|
if (!pgdp)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@@ -502,16 +494,17 @@ static int hisi_attach_dev_lpae(struct iommu_domain *domain, struct device *dev)
|
|||||||
{
|
{
|
||||||
struct device_node *np = dev->of_node;
|
struct device_node *np = dev->of_node;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
struct iommu_domain_data *iommu_info = NULL;
|
struct hisi_domain *hisi_domain = to_hisi_domain(domain);
|
||||||
|
struct iommu_domain_data *domain_data = kzalloc(sizeof
|
||||||
|
(struct iommu_domain_data), GFP_KERNEL);
|
||||||
|
if (!domain_data)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
ret = of_get_iova_info(np, &domain_data->iova_start, &domain_data->iova_size,
|
||||||
|
&domain_data->iova_align);
|
||||||
|
domain_data->phy_pgd_base = hisi_domain->pa_pgtable_addr;
|
||||||
|
domain->priv = domain_data;
|
||||||
|
|
||||||
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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -522,7 +515,6 @@ static void hisi_detach_dev_lpae(struct iommu_domain *domain,
|
|||||||
|
|
||||||
data = (struct iommu_domain_data *)domain->priv;
|
data = (struct iommu_domain_data *)domain->priv;
|
||||||
if (data) {
|
if (data) {
|
||||||
list_del(&data->list);
|
|
||||||
domain->priv = NULL;
|
domain->priv = NULL;
|
||||||
kfree(data);
|
kfree(data);
|
||||||
} else {
|
} else {
|
||||||
@@ -783,28 +775,15 @@ static int hisi_smmu_probe_lpae(struct platform_device *pdev)
|
|||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
|
|
||||||
dbg("enter %s\n", __func__);
|
dbg("enter %s\n", __func__);
|
||||||
|
|
||||||
hisi_smmu_dev = devm_kzalloc(dev,
|
hisi_smmu_dev = devm_kzalloc(dev,
|
||||||
sizeof(struct hisi_smmu_device_lpae), GFP_KERNEL);
|
sizeof(struct hisi_smmu_device_lpae), GFP_KERNEL);
|
||||||
|
if(!hisi_smmu_dev)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
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);
|
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);
|
bus_set_iommu(&platform_bus_type, &hisi_smmu_ops);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
smmu_device_error:
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int hisi_smmu_remove_lpae(struct platform_device *pdev)
|
static int hisi_smmu_remove_lpae(struct platform_device *pdev)
|
||||||
@@ -838,7 +817,7 @@ static int __init hisi_smmu_init_lpae(void)
|
|||||||
|
|
||||||
static void __exit hisi_smmu_exit_lpae(void)
|
static void __exit hisi_smmu_exit_lpae(void)
|
||||||
{
|
{
|
||||||
return platform_driver_unregister(&hisi_smmu_driver_lpae);
|
platform_driver_unregister(&hisi_smmu_driver_lpae);
|
||||||
}
|
}
|
||||||
|
|
||||||
subsys_initcall(hisi_smmu_init_lpae);
|
subsys_initcall(hisi_smmu_init_lpae);
|
||||||
|
|||||||
+114
-63
@@ -32,12 +32,8 @@
|
|||||||
#include <linux/hisi/hisi-iommu.h>
|
#include <linux/hisi/hisi-iommu.h>
|
||||||
#include <linux/hisi/ion-iommu.h>
|
#include <linux/hisi/ion-iommu.h>
|
||||||
|
|
||||||
#ifdef IOMMU_DEBUG
|
#define MAX_IOVA_SIZE_2G 0x80000000UL
|
||||||
#define dbg(format, arg...) \
|
#define MAX_IOVA_START_ADDR_4G 0x100000000UL
|
||||||
pr_info("[hisi_iommu_domain]"format, ##arg)
|
|
||||||
#else
|
|
||||||
#define dbg(format, arg...)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct map_result {
|
struct map_result {
|
||||||
unsigned long iova_start;
|
unsigned long iova_start;
|
||||||
@@ -47,8 +43,8 @@ struct map_result {
|
|||||||
unsigned long is_tile;
|
unsigned long is_tile;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct iommu_domain_data *domain_info;
|
static struct iommu_domain_data *ion_domain_info;
|
||||||
static struct hisi_iommu_domain *m_hisi_domain;
|
static struct ion_iommu_domain *ion_iommu_domain;
|
||||||
DEFINE_MUTEX(iova_pool_mutex);
|
DEFINE_MUTEX(iova_pool_mutex);
|
||||||
|
|
||||||
static unsigned long hisi_alloc_iova(struct gen_pool *pool,
|
static unsigned long hisi_alloc_iova(struct gen_pool *pool,
|
||||||
@@ -83,23 +79,23 @@ static void hisi_free_iova(struct gen_pool *pool,
|
|||||||
|
|
||||||
unsigned long hisi_iommu_alloc_iova(size_t size, unsigned long align)
|
unsigned long hisi_iommu_alloc_iova(size_t size, unsigned long align)
|
||||||
{
|
{
|
||||||
struct hisi_iommu_domain *hisi_domain = m_hisi_domain;
|
struct ion_iommu_domain *ion_domain = ion_iommu_domain;
|
||||||
|
|
||||||
return hisi_alloc_iova(hisi_domain->iova_pool, size, align);
|
return hisi_alloc_iova(ion_domain->iova_pool, size, align);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(hisi_iommu_alloc_iova);
|
EXPORT_SYMBOL_GPL(hisi_iommu_alloc_iova);
|
||||||
|
|
||||||
void hisi_iommu_free_iova(unsigned long iova, size_t size)
|
void hisi_iommu_free_iova(unsigned long iova, size_t size)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
struct hisi_iommu_domain *hisi_domain = m_hisi_domain;
|
struct ion_iommu_domain *ion_domain = ion_iommu_domain;
|
||||||
|
|
||||||
ret = addr_in_gen_pool(hisi_domain->iova_pool, iova, size);
|
ret = addr_in_gen_pool(ion_domain->iova_pool, iova, size);
|
||||||
if(!ret) {
|
if(!ret) {
|
||||||
pr_err("%s:illegal para!!iova = %lx, size = %lx\n",
|
pr_err("%s:illegal para!!iova = %lx, size = %lx\n",
|
||||||
__func__, iova, size);
|
__func__, iova, size);
|
||||||
}
|
}
|
||||||
hisi_free_iova(hisi_domain->iova_pool, iova, size);
|
hisi_free_iova(ion_domain->iova_pool, iova, size);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(hisi_iommu_free_iova);
|
EXPORT_SYMBOL_GPL(hisi_iommu_free_iova);
|
||||||
|
|
||||||
@@ -135,7 +131,41 @@ static void iova_pool_destroy(struct gen_pool *pool)
|
|||||||
gen_pool_destroy(pool);
|
gen_pool_destroy(pool);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int do_iommu_domain_map(struct hisi_iommu_domain *hisi_domain,
|
static int iommu_check_tile_format(struct iommu_map_format *format,
|
||||||
|
unsigned long phys_len)
|
||||||
|
{
|
||||||
|
unsigned long header_size = format->header_size;
|
||||||
|
unsigned long phys_page_line = format->phys_page_line;
|
||||||
|
unsigned long virt_page_line = format->virt_page_line;
|
||||||
|
|
||||||
|
/* check invalid args */
|
||||||
|
if ((phys_len <= header_size) || !phys_page_line || !virt_page_line) {
|
||||||
|
pr_err("[%s]wrong arg:phys_len 0x%lx, header_size 0x%lx\n",
|
||||||
|
__func__, phys_len, header_size);
|
||||||
|
pr_err("[%s]wrong arg:phys line 0x%lx, virt line 0x%lx\n",
|
||||||
|
__func__, phys_page_line, virt_page_line);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* virt_page_line <= 128 and
|
||||||
|
* virt_page_line / phys_page_line = 1 in normal process */
|
||||||
|
if ((virt_page_line * PAGE_SIZE > SZ_512K) ||
|
||||||
|
(virt_page_line / phys_page_line != 1)) {
|
||||||
|
pr_err("%s:virt line/phys line out of standard\n", __func__);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check overflow */
|
||||||
|
if ((virt_page_line * PAGE_SIZE <= virt_page_line) ||
|
||||||
|
(phys_page_line * PAGE_SIZE <= phys_page_line)) {
|
||||||
|
pr_err("%s:virt_line/phys_line too large!!!\n", __func__);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_iommu_domain_map(struct ion_iommu_domain *ion_domain,
|
||||||
struct scatterlist *sgl, struct iommu_map_format *format,
|
struct scatterlist *sgl, struct iommu_map_format *format,
|
||||||
struct map_result *result)
|
struct map_result *result)
|
||||||
{
|
{
|
||||||
@@ -147,6 +177,12 @@ static int do_iommu_domain_map(struct hisi_iommu_domain *hisi_domain,
|
|||||||
struct iommu_domain *domain;
|
struct iommu_domain *domain;
|
||||||
struct scatterlist *sg;
|
struct scatterlist *sg;
|
||||||
struct tile_format fmt;
|
struct tile_format fmt;
|
||||||
|
|
||||||
|
if (format->prot & IOMMU_SEC) {
|
||||||
|
pr_err("prot 0x%lx\n", format->prot);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
/* calculate whole phys mem length */
|
/* calculate whole phys mem length */
|
||||||
for (phys_len = 0, sg = sgl; sg; sg = sg_next(sg))
|
for (phys_len = 0, sg = sgl; sg; sg = sg_next(sg))
|
||||||
phys_len += (unsigned long)ALIGN(sg->length, PAGE_SIZE);
|
phys_len += (unsigned long)ALIGN(sg->length, PAGE_SIZE);
|
||||||
@@ -156,6 +192,10 @@ static int do_iommu_domain_map(struct hisi_iommu_domain *hisi_domain,
|
|||||||
unsigned long lines;
|
unsigned long lines;
|
||||||
unsigned long body_size;
|
unsigned long body_size;
|
||||||
|
|
||||||
|
if (iommu_check_tile_format(format, phys_len)) {
|
||||||
|
pr_err("%s:check foramt fail\n", __func__);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
body_size = phys_len - format->header_size;
|
body_size = phys_len - format->header_size;
|
||||||
lines = body_size / (format->phys_page_line * PAGE_SIZE);
|
lines = body_size / (format->phys_page_line * PAGE_SIZE);
|
||||||
|
|
||||||
@@ -169,15 +209,21 @@ static int do_iommu_domain_map(struct hisi_iommu_domain *hisi_domain,
|
|||||||
/ (format->virt_page_line * PAGE_SIZE);
|
/ (format->virt_page_line * PAGE_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
iova_size = lines * format->virt_page_line * PAGE_SIZE;
|
iova_size = lines * format->virt_page_line * PAGE_SIZE - (format->virt_page_line - format->phys_page_line) * PAGE_SIZE;
|
||||||
} else {
|
} else {
|
||||||
iova_size = phys_len;
|
iova_size = phys_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Limit not exceeding 2G */
|
||||||
|
if (MAX_IOVA_SIZE_2G < iova_size) {
|
||||||
|
pr_err("[%s]hisi_alloc_iova alloc size more than 2G(0x%lx).\n",__func__, iova_size);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
/* alloc iova */
|
/* alloc iova */
|
||||||
pool = hisi_domain->iova_pool;
|
pool = ion_domain->iova_pool;
|
||||||
domain = hisi_domain->domain;
|
domain = ion_domain->domain;
|
||||||
iova_start = hisi_alloc_iova(pool, iova_size, hisi_domain->range.align);
|
iova_start = hisi_alloc_iova(pool, iova_size, ion_domain->range.align);
|
||||||
if (!iova_start) {
|
if (!iova_start) {
|
||||||
pr_err("[%s]hisi_alloc_iova alloc size 0x%lx failed!"
|
pr_err("[%s]hisi_alloc_iova alloc size 0x%lx failed!"
|
||||||
"hisi ion pool avail 0x%lx\n",
|
"hisi ion pool avail 0x%lx\n",
|
||||||
@@ -185,7 +231,7 @@ static int do_iommu_domain_map(struct hisi_iommu_domain *hisi_domain,
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (0x100000000 < (iova_start + iova_size)) {
|
if (MAX_IOVA_START_ADDR_4G < (iova_start + iova_size)) {
|
||||||
pr_err("hisi iommu can not deal with iova 0x%lx size 0x%lx\n",
|
pr_err("hisi iommu can not deal with iova 0x%lx size 0x%lx\n",
|
||||||
iova_start, iova_size);
|
iova_start, iova_size);
|
||||||
}
|
}
|
||||||
@@ -222,15 +268,15 @@ int hisi_iommu_map_domain(struct scatterlist *sgl,
|
|||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
struct map_result result;
|
struct map_result result;
|
||||||
struct hisi_iommu_domain *hisi_domain;
|
struct ion_iommu_domain *ion_domain;
|
||||||
|
|
||||||
hisi_domain = m_hisi_domain;
|
ion_domain = ion_iommu_domain;
|
||||||
|
|
||||||
memset(&result, 0, sizeof(result));
|
memset(&result, 0, sizeof(result));
|
||||||
|
|
||||||
ret = do_iommu_domain_map(hisi_domain, sgl, format, &result);
|
ret = do_iommu_domain_map(ion_domain, sgl, format, &result);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dbg("alloc iova fail\n");
|
pr_err("alloc iova fail\n");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
format->iova_start = result.iova_start;
|
format->iova_start = result.iova_start;
|
||||||
@@ -245,8 +291,8 @@ static int do_iommu_domain_unmap(struct map_result *result)
|
|||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
unsigned long unmaped_size;
|
unsigned long unmaped_size;
|
||||||
struct hisi_iommu_domain *hisi_domain = m_hisi_domain;
|
struct ion_iommu_domain *ion_domain = ion_iommu_domain;
|
||||||
struct gen_pool *pool = hisi_domain->iova_pool;
|
struct gen_pool *pool = ion_domain->iova_pool;
|
||||||
|
|
||||||
/* never unmap a zero length address space */
|
/* never unmap a zero length address space */
|
||||||
if (!result->iova_size) {
|
if (!result->iova_size) {
|
||||||
@@ -257,15 +303,15 @@ static int do_iommu_domain_unmap(struct map_result *result)
|
|||||||
|
|
||||||
/* unmap tile equals to unmpa range */
|
/* unmap tile equals to unmpa range */
|
||||||
if (result->is_tile) {
|
if (result->is_tile) {
|
||||||
unmaped_size = iommu_unmap_tile(hisi_domain->domain,
|
unmaped_size = iommu_unmap_tile(ion_domain->domain,
|
||||||
result->iova_start, result->iova_size);
|
result->iova_start, result->iova_size);
|
||||||
} else {
|
} else {
|
||||||
unmaped_size = iommu_unmap(hisi_domain->domain,
|
unmaped_size = iommu_unmap(ion_domain->domain,
|
||||||
result->iova_start, result->iova_size);
|
result->iova_start, result->iova_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unmaped_size != result->iova_size) {
|
if (unmaped_size != result->iova_size) {
|
||||||
dbg("[%s]unmap failed!\n", __func__);
|
pr_err("[%s]unmap failed!\n", __func__);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
/* free iova */
|
/* free iova */
|
||||||
@@ -301,65 +347,70 @@ int hisi_iommu_unmap_domain(struct iommu_map_format *format)
|
|||||||
EXPORT_SYMBOL_GPL(hisi_iommu_unmap_domain);
|
EXPORT_SYMBOL_GPL(hisi_iommu_unmap_domain);
|
||||||
|
|
||||||
/*only used to test*/
|
/*only used to test*/
|
||||||
phys_addr_t hisi_iommu_domain_iova_to_phys(unsigned long iova)
|
phys_addr_t ion_iommu_domain_iova_to_phys(unsigned long iova)
|
||||||
{
|
{
|
||||||
struct iommu_domain *domain;
|
struct iommu_domain *domain;
|
||||||
domain = m_hisi_domain->domain;
|
domain = ion_iommu_domain->domain;
|
||||||
return iommu_iova_to_phys(domain, iova);
|
return iommu_iova_to_phys(domain, iova);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(hisi_iommu_domain_iova_to_phys);
|
EXPORT_SYMBOL_GPL(ion_iommu_domain_iova_to_phys);
|
||||||
|
|
||||||
int hisi_ion_enable_iommu(struct platform_device *pdev)
|
struct iommu_domain *hisi_ion_enable_iommu(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
int ret;
|
|
||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
struct hisi_iommu_domain *hisi_domain;
|
struct ion_iommu_domain *ion_domain;
|
||||||
|
|
||||||
pr_info("in %s start\n", __func__);
|
pr_info("in %s start\n", __func__);
|
||||||
hisi_domain = kzalloc(sizeof(*hisi_domain), GFP_KERNEL);
|
if (ion_iommu_domain) {
|
||||||
if (!hisi_domain) {
|
pr_err("ion domain already init return domain\n");
|
||||||
dbg("alloc hisi_domain object fail\n");
|
return ion_iommu_domain->domain;
|
||||||
return -ENOMEM;
|
}
|
||||||
|
|
||||||
|
ion_domain = kzalloc(sizeof(*ion_domain), GFP_KERNEL);
|
||||||
|
if (!ion_domain) {
|
||||||
|
pr_err("alloc ion_domain object fail\n");
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!iommu_present(dev->bus)) {
|
if (!iommu_present(dev->bus)) {
|
||||||
dbg("iommu not found\n");
|
pr_err("iommu not found\n");
|
||||||
kfree(hisi_domain);
|
kfree(ion_domain);
|
||||||
return 0;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* create iommu domain */
|
/* create iommu domain */
|
||||||
hisi_domain->domain = iommu_domain_alloc(dev->bus);
|
ion_domain->domain = iommu_domain_alloc(dev->bus);
|
||||||
if (!hisi_domain->domain) {
|
if (!ion_domain->domain)
|
||||||
ret = -EINVAL;
|
|
||||||
goto error;
|
goto error;
|
||||||
}
|
|
||||||
iommu_attach_device(hisi_domain->domain, dev);
|
iommu_attach_device(ion_domain->domain, dev);
|
||||||
domain_info = (struct iommu_domain_data *)hisi_domain->domain->priv;
|
ion_domain_info = (struct iommu_domain_data *)ion_domain->domain->priv;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Current align is 256K
|
* Current align is 256K
|
||||||
*/
|
*/
|
||||||
hisi_domain->iova_pool = iova_pool_setup(domain_info->iova_start,
|
ion_domain->iova_pool = iova_pool_setup(ion_domain_info->iova_start,
|
||||||
domain_info->iova_size, domain_info->iova_align);
|
ion_domain_info->iova_size, ion_domain_info->iova_align);
|
||||||
if (!hisi_domain->iova_pool) {
|
if (!ion_domain->iova_pool)
|
||||||
ret = -EINVAL;
|
|
||||||
goto error;
|
goto error;
|
||||||
}
|
|
||||||
/* this is a global pointer */
|
|
||||||
m_hisi_domain = hisi_domain;
|
|
||||||
|
|
||||||
dbg("in %s end\n", __func__);
|
/* this is a global pointer */
|
||||||
return 0;
|
ion_iommu_domain = ion_domain;
|
||||||
|
|
||||||
|
pr_info("in %s end\n", __func__);
|
||||||
|
return ion_iommu_domain->domain;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
WARN(1, "hisi_iommu_domain_init failed!\n");
|
WARN(1, "ion_iommu_domain_init failed!\n");
|
||||||
if (hisi_domain->iova_pool)
|
if (ion_domain->iova_pool)
|
||||||
iova_pool_destroy(hisi_domain->iova_pool);
|
iova_pool_destroy(ion_domain->iova_pool);
|
||||||
if (hisi_domain->domain)
|
|
||||||
iommu_domain_free(hisi_domain->domain);
|
|
||||||
kfree(hisi_domain);
|
|
||||||
|
|
||||||
return ret;
|
if (ion_domain->domain)
|
||||||
|
iommu_domain_free(ion_domain->domain);
|
||||||
|
|
||||||
|
kfree(ion_domain);
|
||||||
|
|
||||||
|
ion_iommu_domain = NULL;
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(hisi_ion_enable_iommu);
|
EXPORT_SYMBOL(hisi_ion_enable_iommu);
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/hisi/hisi_ion.h>
|
#include <linux/hisi/hisi_ion.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/hisi/hisi-iommu.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/mm.h>
|
#include <linux/mm.h>
|
||||||
@@ -165,7 +166,6 @@ static long hisi_ion_custom_ioctl(struct ion_client *client,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern int hisi_ion_enable_iommu(struct platform_device *pdev);
|
|
||||||
|
|
||||||
static int get_type_by_name(const char *name, enum ion_heap_type *type)
|
static int get_type_by_name(const char *name, enum ion_heap_type *type)
|
||||||
{
|
{
|
||||||
@@ -233,7 +233,7 @@ static int hisi_set_platform_data(struct platform_device *pdev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!prop->value || !prop->length) {
|
if (!prop->value || !prop->length) {
|
||||||
pr_err("%s %s %d, node %s, invalid phandle, value=%p,length=%d\n",
|
pr_err("%s %s %d, node %s, invalid phandle, value=%pK,length=%d\n",
|
||||||
__FILE__,__FUNCTION__,__LINE__,
|
__FILE__,__FUNCTION__,__LINE__,
|
||||||
np->name, prop->value, prop->length );
|
np->name, prop->value, prop->length );
|
||||||
continue;
|
continue;
|
||||||
@@ -337,7 +337,7 @@ static int hisi_ion_probe(struct platform_device *pdev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* FIXME will move to iommu driver*/
|
/* FIXME will move to iommu driver*/
|
||||||
if (hisi_ion_enable_iommu(pdev)) {
|
if (!hisi_ion_enable_iommu(pdev)) {
|
||||||
dev_info(&pdev->dev, "enable iommu fail \n");
|
dev_info(&pdev->dev, "enable iommu fail \n");
|
||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
goto err_free_idev;
|
goto err_free_idev;
|
||||||
|
|||||||
@@ -2,9 +2,16 @@
|
|||||||
#define _HI36XX_SMMU_H
|
#define _HI36XX_SMMU_H
|
||||||
|
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
|
#include <linux/iommu.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
|
||||||
|
extern struct iommu_domain *hisi_ion_enable_iommu(struct platform_device *pdev);
|
||||||
|
extern int of_get_iova_info(struct device_node *np, unsigned long *iova_start,
|
||||||
|
unsigned long *iova_size, unsigned long *iova_align);
|
||||||
|
|
||||||
struct iommu_domain_data {
|
struct iommu_domain_data {
|
||||||
unsigned int iova_start;
|
unsigned long iova_start;
|
||||||
unsigned int iova_size;
|
unsigned long iova_size;
|
||||||
phys_addr_t phy_pgd_base;
|
phys_addr_t phy_pgd_base;
|
||||||
unsigned long iova_align;
|
unsigned long iova_align;
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ struct section_info {
|
|||||||
unsigned int page_size;
|
unsigned int page_size;
|
||||||
unsigned int align;
|
unsigned int align;
|
||||||
};
|
};
|
||||||
struct hisi_iommu_domain {
|
struct ion_iommu_domain {
|
||||||
struct iommu_domain *domain;
|
struct iommu_domain *domain;
|
||||||
struct gen_pool *iova_pool;
|
struct gen_pool *iova_pool;
|
||||||
struct section_info range;
|
struct section_info range;
|
||||||
@@ -26,7 +26,6 @@ struct hisi_iommu_domain * hisi_get_domain(void);
|
|||||||
size_t hisi_iommu_iova_size(void);
|
size_t hisi_iommu_iova_size(void);
|
||||||
size_t hisi_iommu_iova_available(void);
|
size_t hisi_iommu_iova_available(void);
|
||||||
void hisi_iommu_free_iova(unsigned long iova, size_t size);
|
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,
|
int hisi_iommu_map_range(struct iommu_domain *domain,unsigned long iova_start, struct scatterlist *sgl,
|
||||||
unsigned long iova_size,unsigned int prot);
|
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_unmap_range(struct iommu_domain *domain,unsigned long iova_start,unsigned long iova_size);
|
||||||
|
|||||||
Reference in New Issue
Block a user