diff options
| author | Yuval Adam <_@yuv.al> | 2017-08-30 08:25:59 +0000 |
|---|---|---|
| committer | Yuval Adam <_@yuv.al> | 2017-08-30 08:25:59 +0000 |
| commit | 8e4bff4cab0ddac6060645b0715210484d02ff40 (patch) | |
| tree | 3a5c3024371a04692a3a6d9974d001cdff8ebf84 /drivers/gpu/sprd_iommu | |
Diffstat (limited to 'drivers/gpu/sprd_iommu')
| -rw-r--r-- | drivers/gpu/sprd_iommu/Kconfig | 5 | ||||
| -rw-r--r-- | drivers/gpu/sprd_iommu/Makefile | 2 | ||||
| -rw-r--r-- | drivers/gpu/sprd_iommu/gsp/Makefile | 1 | ||||
| -rwxr-xr-x | drivers/gpu/sprd_iommu/gsp/sprd_iommu_gsp.c | 229 | ||||
| -rw-r--r-- | drivers/gpu/sprd_iommu/mm/Makefile | 1 | ||||
| -rw-r--r-- | drivers/gpu/sprd_iommu/mm/sprd_iommu_mm.c | 268 | ||||
| -rw-r--r-- | drivers/gpu/sprd_iommu/sprd_iommu_common.c | 349 | ||||
| -rw-r--r-- | drivers/gpu/sprd_iommu/sprd_iommu_common.h | 144 | ||||
| -rw-r--r-- | drivers/gpu/sprd_iommu/sprd_iommu_drv.c | 432 | ||||
| -rw-r--r-- | drivers/gpu/sprd_iommu/sprd_iommu_sysfs.c | 157 | ||||
| -rw-r--r-- | drivers/gpu/sprd_iommu/sprd_iommu_sysfs.h | 12 | ||||
| -rw-r--r-- | drivers/gpu/sprd_iommu/v2/Makefile | 6 | ||||
| -rw-r--r-- | drivers/gpu/sprd_iommu/v2/sprd_iommu_dcam.c | 192 | ||||
| -rw-r--r-- | drivers/gpu/sprd_iommu/v2/sprd_iommu_dispc.c | 229 | ||||
| -rw-r--r-- | drivers/gpu/sprd_iommu/v2/sprd_iommu_gsp0.c | 229 | ||||
| -rw-r--r-- | drivers/gpu/sprd_iommu/v2/sprd_iommu_gsp1.c | 229 | ||||
| -rw-r--r-- | drivers/gpu/sprd_iommu/v2/sprd_iommu_vpp.c | 192 | ||||
| -rw-r--r-- | drivers/gpu/sprd_iommu/v2/sprd_iommu_vsp.c | 192 |
18 files changed, 2869 insertions, 0 deletions
diff --git a/drivers/gpu/sprd_iommu/Kconfig b/drivers/gpu/sprd_iommu/Kconfig new file mode 100644 index 00000000..b3fb2eaa --- /dev/null +++ b/drivers/gpu/sprd_iommu/Kconfig @@ -0,0 +1,5 @@ +config SPRD_IOMMU + bool "SPRD IOMMU Module" + help + Chose this option to enable the SPRD IOMMU. + diff --git a/drivers/gpu/sprd_iommu/Makefile b/drivers/gpu/sprd_iommu/Makefile new file mode 100644 index 00000000..a51b6ffe --- /dev/null +++ b/drivers/gpu/sprd_iommu/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_SPRD_IOMMU) += sprd_iommu_sysfs.o sprd_iommu_drv.o +obj-$(CONFIG_SPRD_IOMMU) += sprd_iommu_common.o gsp/ mm/ v2/ diff --git a/drivers/gpu/sprd_iommu/gsp/Makefile b/drivers/gpu/sprd_iommu/gsp/Makefile new file mode 100644 index 00000000..a3356432 --- /dev/null +++ b/drivers/gpu/sprd_iommu/gsp/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_SPRD_IOMMU) += sprd_iommu_gsp.o diff --git a/drivers/gpu/sprd_iommu/gsp/sprd_iommu_gsp.c b/drivers/gpu/sprd_iommu/gsp/sprd_iommu_gsp.c new file mode 100755 index 00000000..4d719372 --- /dev/null +++ b/drivers/gpu/sprd_iommu/gsp/sprd_iommu_gsp.c @@ -0,0 +1,229 @@ +/* + * drivers/gpu/iommu/iommu.c * + * Copyright (C) 2011 Google, Inc. + * + * 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. + * + */ + +#include"../sprd_iommu_common.h" + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void sprd_iommu_gsp_early_suspend(struct early_suspend *es) +{ + int err = -1; + + struct sprd_iommu_dev *dev = container_of(es, + struct sprd_iommu_dev, early_suspend); + + mutex_lock(&dev->mutex_map); + pr_info("%s, map_count:%d\n", __func__, dev->map_count); + if (dev->map_count > 0) + err = sprd_iommu_backup(dev); + mutex_unlock(&dev->mutex_map); +} + +static void sprd_iommu_gsp_late_resume(struct early_suspend *es) +{ + int err = -1; + + struct sprd_iommu_dev *dev = container_of(es, + struct sprd_iommu_dev, early_suspend); + + mutex_lock(&dev->mutex_map); + pr_info("%s, map_count:%d\n", __func__, dev->map_count); + if (dev->map_count > 0) + err = sprd_iommu_restore(dev); + mutex_unlock(&dev->mutex_map); +} +#endif + +void sprd_iommu_gsp_clk_enable(struct sprd_iommu_dev *dev) +{ + pr_debug("%s\n", __func__); +#ifndef CONFIG_SC_FPGA + clk_prepare_enable(dev->mmu_mclock); + clk_prepare_enable(dev->mmu_clock); +#endif +} + +void sprd_iommu_gsp_clk_disable(struct sprd_iommu_dev *dev) +{ + pr_debug("%s\n", __func__); +#ifndef CONFIG_SC_FPGA + clk_disable_unprepare(dev->mmu_clock); + clk_disable_unprepare(dev->mmu_mclock); +#endif +} + +void sprd_iommu_gsp_open(struct sprd_iommu_dev *dev) +{ +} + +void sprd_iommu_gsp_release(struct sprd_iommu_dev *dev) +{ +} + +int sprd_iommu_gsp_init(struct sprd_iommu_dev *dev, + struct sprd_iommu_init_data *data) +{ + int err = -1; +#ifndef CONFIG_SC_FPGA + struct device_node *np; + + np = dev->misc_dev.this_device->of_node; + if (!np) + return -1; + + dev->mmu_mclock = of_clk_get(np, 0); + dev->mmu_clock = of_clk_get(np, 2); + + if (IS_ERR(dev->mmu_clock) || IS_ERR(dev->mmu_mclock)) { + pr_info("%s, can't get clock:%p, %p\n", __func__, + dev->mmu_clock, dev->mmu_mclock); + goto errorout; + } +#endif +#ifdef CONFIG_HAS_EARLYSUSPEND + dev->early_suspend.suspend = sprd_iommu_gsp_early_suspend; + dev->early_suspend.resume = sprd_iommu_gsp_late_resume; + dev->early_suspend.level = EARLY_SUSPEND_LEVEL_STOP_DRAWING; + register_early_suspend(&dev->early_suspend); +#endif + + sprd_iommu_gsp_clk_enable(dev); + err = sprd_iommu_init(dev, data); + sprd_iommu_gsp_clk_disable(dev); + + return err; + +#ifndef CONFIG_SC_FPGA +errorout: + if (dev->mmu_clock) + clk_put(dev->mmu_clock); + + if (dev->mmu_mclock) + clk_put(dev->mmu_mclock); + + return -1; +#endif +} + +int sprd_iommu_gsp_exit(struct sprd_iommu_dev *dev) +{ + int err = -1; + + sprd_iommu_gsp_clk_enable(dev); + err = sprd_iommu_exit(dev); + sprd_iommu_gsp_clk_disable(dev); + + return err; +} + +unsigned long sprd_iommu_gsp_iova_alloc(struct sprd_iommu_dev *dev, + size_t iova_length) +{ + return sprd_iommu_iova_alloc(dev, iova_length); +} + +void sprd_iommu_gsp_iova_free(struct sprd_iommu_dev *dev, unsigned long iova, + size_t iova_length) +{ + sprd_iommu_iova_free(dev, iova, iova_length); +} + +int sprd_iommu_gsp_iova_map(struct sprd_iommu_dev *dev, unsigned long iova, + size_t iova_length, struct sg_table *table) +{ + int err = -1; + + mutex_lock(&dev->mutex_map); + if (0 == dev->map_count) + sprd_iommu_enable(dev); + dev->map_count++; + + err = sprd_iommu_iova_map(dev, iova, iova_length, table); + mutex_unlock(&dev->mutex_map); + + return err; +} + +int sprd_iommu_gsp_iova_unmap(struct sprd_iommu_dev *dev, unsigned long iova, + size_t iova_length) +{ + int err = -1; + + mutex_lock(&dev->mutex_map); + err = sprd_iommu_iova_unmap(dev, iova, iova_length); + + dev->map_count--; + if (0 == dev->map_count) + sprd_iommu_disable(dev); + mutex_unlock(&dev->mutex_map); + + return err; +} + +int sprd_iommu_gsp_backup(struct sprd_iommu_dev *dev) +{ +#ifdef CONFIG_HAS_EARLYSUSPEND + return 0; +#else + int err = -1; + + mutex_lock(&dev->mutex_map); + pr_info("%s, map_count:%d\n", __func__, dev->map_count); + if (dev->map_count > 0) + err = sprd_iommu_backup(dev); + mutex_unlock(&dev->mutex_map); + + return err; +#endif +} + +int sprd_iommu_gsp_restore(struct sprd_iommu_dev *dev) +{ +#ifdef CONFIG_HAS_EARLYSUSPEND + return 0; +#else + int err = -1; + + mutex_lock(&dev->mutex_map); + pr_info("%s, map_count:%d\n", __func__, dev->map_count); + if (dev->map_count > 0) + err = sprd_iommu_restore(dev); + mutex_unlock(&dev->mutex_map); + + return 0; +#endif +} + +int sprd_iommu_gsp_dump(struct sprd_iommu_dev *dev, unsigned long iova, + size_t iova_length) +{ + return sprd_iommu_dump(dev, iova, iova_length); +} + +struct sprd_iommu_ops iommu_gsp_ops = { + .init = sprd_iommu_gsp_init, + .exit = sprd_iommu_gsp_exit, + .iova_alloc = sprd_iommu_gsp_iova_alloc, + .iova_free = sprd_iommu_gsp_iova_free, + .iova_map = sprd_iommu_gsp_iova_map, + .iova_unmap = sprd_iommu_gsp_iova_unmap, + .backup = sprd_iommu_gsp_backup, + .restore = sprd_iommu_gsp_restore, + .enable = sprd_iommu_gsp_clk_enable, + .disable = sprd_iommu_gsp_clk_disable, + .dump = sprd_iommu_gsp_dump, + .open = sprd_iommu_gsp_open, + .release = sprd_iommu_gsp_release, +}; + diff --git a/drivers/gpu/sprd_iommu/mm/Makefile b/drivers/gpu/sprd_iommu/mm/Makefile new file mode 100644 index 00000000..f49a0180 --- /dev/null +++ b/drivers/gpu/sprd_iommu/mm/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_SPRD_IOMMU) += sprd_iommu_mm.o diff --git a/drivers/gpu/sprd_iommu/mm/sprd_iommu_mm.c b/drivers/gpu/sprd_iommu/mm/sprd_iommu_mm.c new file mode 100644 index 00000000..c5ff6a24 --- /dev/null +++ b/drivers/gpu/sprd_iommu/mm/sprd_iommu_mm.c @@ -0,0 +1,268 @@ +/* + * drivers/gpu/iommu/iommu.c * + * Copyright (C) 2011 Google, Inc. + * + * 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. + * + */ + +#include"../sprd_iommu_common.h" +#include <linux/clk-provider.h> + +static void sprd_iommu_mm_prepare(struct sprd_iommu_dev *dev) +{ +#ifndef CONFIG_SC_FPGA + pr_info("%s, axi clock:%p\n", __func__, dev->mmu_axiclock); + if (dev->mmu_axiclock) + clk_prepare(dev->mmu_axiclock); +#endif +} + +static void sprd_iommu_mm_unprepare(struct sprd_iommu_dev *dev) +{ +#ifndef CONFIG_SC_FPGA + pr_info("%s, axi clock:%p\n", __func__, dev->mmu_axiclock); + if (dev->mmu_axiclock) + clk_unprepare(dev->mmu_axiclock); +#endif +} + +void sprd_iommu_mm_clk_enable(struct sprd_iommu_dev *dev) +{ + if (!dev->light_sleep_en) + pr_info("%s\n", __func__); +#ifndef CONFIG_SC_FPGA + clk_prepare_enable(dev->mmu_mclock); + clk_prepare_enable(dev->mmu_clock); +#endif +} + +void sprd_iommu_mm_clk_disable(struct sprd_iommu_dev *dev) +{ + if (!dev->light_sleep_en) + pr_info("%s\n", __func__); +#ifndef CONFIG_SC_FPGA + clk_disable_unprepare(dev->mmu_clock); + clk_disable_unprepare(dev->mmu_mclock); +#endif +} + +void sprd_iommu_mm_open(struct sprd_iommu_dev *dev) +{ +} + +void sprd_iommu_mm_release(struct sprd_iommu_dev *dev) +{ +} + +int sprd_iommu_mm_init(struct sprd_iommu_dev *dev, + struct sprd_iommu_init_data *data) +{ + int err = -1; +#ifndef CONFIG_SC_FPGA + struct device_node *np; + + np = dev->misc_dev.this_device->of_node; + if (!np) + return -1; + + dev->mmu_clock = of_clk_get(np, 0); + dev->mmu_mclock = of_clk_get(np, 1); + dev->mmu_axiclock = of_clk_get(np, 2); + if (IS_ERR(dev->mmu_axiclock)) + pr_info("%s, can't get mm axi clock:%p\n", __func__, + dev->mmu_axiclock); + else + dev->light_sleep_en = true; + + if (IS_ERR(dev->mmu_clock) || IS_ERR(dev->mmu_mclock)) { + pr_info("%s, can't get clock:%p, %p\n", __func__, + dev->mmu_clock, dev->mmu_mclock); + goto errorout; + } +#endif + sprd_iommu_mm_clk_enable(dev); + err = sprd_iommu_init(dev, data); + sprd_iommu_mm_clk_disable(dev); + + return err; + +#ifndef CONFIG_SC_FPGA +errorout: + if (dev->mmu_clock) + clk_put(dev->mmu_clock); + + if (dev->mmu_mclock) + clk_put(dev->mmu_mclock); + + if (dev->mmu_axiclock) + clk_put(dev->mmu_axiclock); + + return -1; +#endif +} + +int sprd_iommu_mm_exit(struct sprd_iommu_dev *dev) +{ + int err = -1; + + sprd_iommu_mm_clk_enable(dev); + err = sprd_iommu_exit(dev); + sprd_iommu_mm_clk_disable(dev); + + return err; +} + +unsigned long sprd_iommu_mm_iova_alloc(struct sprd_iommu_dev *dev, + size_t iova_length) +{ + return sprd_iommu_iova_alloc(dev, iova_length); +} + +void sprd_iommu_mm_iova_free(struct sprd_iommu_dev *dev, unsigned long iova, + size_t iova_length) +{ + sprd_iommu_iova_free(dev, iova, iova_length); +} + +int sprd_iommu_mm_iova_map(struct sprd_iommu_dev *dev, unsigned long iova, + size_t iova_length, struct sg_table *table) +{ + int err = -1; + + if (dev->light_sleep_en) { + mutex_lock(&dev->mutex_map); + if (0 == dev->map_count) + sprd_iommu_mm_prepare(dev); + + sprd_iommu_mm_clk_enable(dev); + + if (0 == dev->map_count) + sprd_iommu_reset_enable(dev); + dev->map_count++; + + err = sprd_iommu_iova_map(dev, iova, iova_length, table); + sprd_iommu_mm_clk_disable(dev); + mutex_unlock(&dev->mutex_map); + } else { + mutex_lock(&dev->mutex_map); + if (0 == dev->map_count) { + sprd_iommu_mm_clk_enable(dev); + sprd_iommu_reset_enable(dev); + } + dev->map_count++; + err = sprd_iommu_iova_map(dev, iova, iova_length, table); + mutex_unlock(&dev->mutex_map); + } + return err; +} + +int sprd_iommu_mm_iova_unmap(struct sprd_iommu_dev *dev, unsigned long iova, + size_t iova_length) +{ + int err = -1; + + if (dev->light_sleep_en) { + mutex_lock(&dev->mutex_map); + sprd_iommu_mm_clk_enable(dev); + err = sprd_iommu_iova_unmap(dev, iova, iova_length); + + dev->map_count--; + if (0 == dev->map_count) + sprd_iommu_disable(dev); + + sprd_iommu_mm_clk_disable(dev); + + if (0 == dev->map_count) + sprd_iommu_mm_unprepare(dev); + mutex_unlock(&dev->mutex_map); + } else { + mutex_lock(&dev->mutex_map); + err = sprd_iommu_iova_unmap(dev, iova, iova_length); + dev->map_count--; + if (0 == dev->map_count) { + sprd_iommu_disable(dev); + sprd_iommu_mm_clk_disable(dev); + } + mutex_unlock(&dev->mutex_map); + } + return err; +} + +int sprd_iommu_mm_backup(struct sprd_iommu_dev *dev) +{ + int err = 0; + + mutex_lock(&dev->mutex_map); + pr_info("%s, map_count:%d\n", __func__, dev->map_count); + + if (dev->light_sleep_en) { + if (dev->map_count > 0) { + sprd_iommu_mm_clk_enable(dev); + err = sprd_iommu_backup(dev); + sprd_iommu_mm_clk_disable(dev); + sprd_iommu_mm_unprepare(dev); + } + } else { + if (dev->map_count > 0) { + err = sprd_iommu_backup(dev); + sprd_iommu_mm_clk_disable(dev); + } + } + mutex_unlock(&dev->mutex_map); + return err; +} + +int sprd_iommu_mm_restore(struct sprd_iommu_dev *dev) +{ + int err = 0; + + mutex_lock(&dev->mutex_map); + pr_info("%s, map_count:%d\n", __func__, dev->map_count); + + if (dev->light_sleep_en) { + if (dev->map_count > 0) { + sprd_iommu_mm_prepare(dev); + sprd_iommu_mm_clk_enable(dev); + err = sprd_iommu_restore(dev); + sprd_iommu_mm_clk_disable(dev); + } + } else { + if (dev->map_count > 0) { + sprd_iommu_mm_clk_enable(dev); + err = sprd_iommu_restore(dev); + } + } + mutex_unlock(&dev->mutex_map); + return err; +} + +int sprd_iommu_mm_dump(struct sprd_iommu_dev *dev, unsigned long iova, + size_t iova_length) +{ + return sprd_iommu_dump(dev, iova, iova_length); +} + +struct sprd_iommu_ops iommu_mm_ops = { + .init = sprd_iommu_mm_init, + .exit = sprd_iommu_mm_exit, + .iova_alloc = sprd_iommu_mm_iova_alloc, + .iova_free = sprd_iommu_mm_iova_free, + .iova_map = sprd_iommu_mm_iova_map, + .iova_unmap = sprd_iommu_mm_iova_unmap, + .backup = sprd_iommu_mm_backup, + .restore = sprd_iommu_mm_restore, + .enable = sprd_iommu_mm_clk_enable, + .disable = sprd_iommu_mm_clk_disable, + .dump = sprd_iommu_mm_dump, + .open = sprd_iommu_mm_open, + .release = sprd_iommu_mm_release, +}; + diff --git a/drivers/gpu/sprd_iommu/sprd_iommu_common.c b/drivers/gpu/sprd_iommu/sprd_iommu_common.c new file mode 100644 index 00000000..0092bf61 --- /dev/null +++ b/drivers/gpu/sprd_iommu/sprd_iommu_common.c @@ -0,0 +1,349 @@ +#include "sprd_iommu_common.h" + +inline void mmu_reg_write(ulong reg, ulong val, ulong msk) +{ + __raw_writel((__raw_readl((void *)reg) & ~msk) | val, (void *)reg); +} + +static inline void __set_pte(struct sprd_iommu_init_data *data, + ulong base, u32 offset, ulong paddr) +{ + /*64-bit virtual addr and 32bit phy addr in arm64.*/ + if (data->iommu_rev == 2) + ((u32 *)base)[offset] = (u32)(paddr >> 12); + else + ((u32 *)base)[offset] = (u32)paddr; +} + +ulong get_phys_addr(struct scatterlist *sg) +{ + /* + * Try sg_dma_address first so that we can + * map carveout regions that do not have a + * struct page associated with them. + */ + ulong pa = sg_dma_address(sg); + if (pa == 0) + pa = sg_phys(sg); + return pa; +} + +static void sprd_iommu_pgt_reset(struct sprd_iommu_init_data *data) +{ + ulong fault_page = data->fault_page; + int i; + + if (fault_page) { + for (i = 0; i < data->pgt_size / sizeof(u32); i++) + __set_pte(data, data->pgt_base, i, fault_page); + } else { + memset((void *)data->pgt_base, 0xFF, data->pgt_size); + } +} + +static inline void sprd_iommu_pgt_update(struct sprd_iommu_init_data *data, + ulong iova, ulong pa, size_t size) +{ + ulong end_iova = iova + size; + for (; iova < end_iova; iova += MMU_PAGE_SIZE, pa += MMU_PAGE_SIZE) { + __set_pte(data, data->pgt_base, MMU_PTE_OFFSET(iova), pa); + } +} + +static inline void sprd_iommu_pgt_clear(struct sprd_iommu_init_data *data, + ulong iova, size_t size) +{ + ulong pgt_start = (ulong)((u32 *)data->pgt_base + MMU_PTE_OFFSET(iova)); + + pr_debug("%s, iova:0x%lx, size:0x%zx, fault_page:0x%lx\n", __func__, + iova, size, data->fault_page); + + if (data->fault_page) { + ulong end_iova = iova + size; + ulong fault_page = data->fault_page; + + for (; iova < end_iova; iova += MMU_PAGE_SIZE) + __set_pte(data, data->pgt_base, MMU_PTE_OFFSET(iova), + fault_page); + } else { + if (pgt_start % sizeof(ulong)) { + *((u32 *)pgt_start) = (u32)-1; + memset((void *)((u32 *)pgt_start + 1), 0xFF, + MMU_IOVA_SIZE_TO_PTE(size - MMU_PAGE_SIZE)); + } else { + memset((void *)pgt_start, 0xFF, + MMU_IOVA_SIZE_TO_PTE(size)); + } + } + +} + +static void sprd_iommu_reg_init(struct sprd_iommu_init_data *data) +{ + if (data->re_route_page) { + mmu_reg_write(data->ctrl_reg + OFFSET_RR_DEST_HI, + (u32)(data->re_route_page >> 32), + MMU_START_MB_ADDR_HI_MASK); + mmu_reg_write(data->ctrl_reg + OFFSET_RR_DEST_LO, + (u32)(data->re_route_page), + MMU_START_MB_ADDR_LO_MASK); + mmu_reg_write(data->ctrl_reg + OFFSET_CFG, + MMU_RR_EN(1), + MMU_RR_EN_MASK); + } + + if (data->iommu_rev == 2) { + mmu_reg_write(data->ctrl_reg + OFFSET_START_MB_ADDR_HI, + (u32)(data->iova_base >> 32), + MMU_START_MB_ADDR_HI_MASK); + mmu_reg_write(data->ctrl_reg + OFFSET_START_MB_ADDR_LO, + (u32)(data->iova_base), + MMU_START_MB_ADDR_LO_MASK); + + mmu_reg_write(data->ctrl_reg + OFFSET_CFG, + MMU_MON_EN(1), + MMU_MON_EN_MASK); + } else { +#ifdef CONFIG_ARCH_SCX35L + mmu_reg_write(data->ctrl_reg, + MMU_V1_RAMCLK_DIV2_EN(1), + MMU_V1_RAMCLK_DIV2_EN_MASK); +#endif + mmu_reg_write(data->ctrl_reg, + data->iova_base, + MMU_START_MB_ADDR_MASK); + } + + mmu_reg_write(data->ctrl_reg, MMU_TLB_EN(1), MMU_TLB_EN_MASK); + mmu_reg_write(data->ctrl_reg, MMU_EN(1), MMU_EN_MASK); +} + +static void sprd_iommu_reg_clear(struct sprd_iommu_init_data *data) +{ + if (data->iommu_rev == 2) { + mmu_reg_write(data->ctrl_reg + OFFSET_CFG, + MMU_RR_EN(0), + MMU_RR_EN_MASK); + mmu_reg_write(data->ctrl_reg + OFFSET_CFG, + MMU_MON_EN(0), + MMU_MON_EN_MASK); + } else { + mmu_reg_write(data->ctrl_reg, + MMU_V1_RAMCLK_DIV2_EN(0), + MMU_V1_RAMCLK_DIV2_EN_MASK); + } + + mmu_reg_write(data->ctrl_reg, MMU_TLB_EN(0), MMU_TLB_EN_MASK); + mmu_reg_write(data->ctrl_reg, MMU_EN(0), MMU_EN_MASK); +} + +int sprd_iommu_init(struct sprd_iommu_dev *dev, + struct sprd_iommu_init_data *data) +{ + int i; + + pr_info("%s, %s, id:%d, iova_base:0x%lx, size:0x%zx\n", __func__, + data->name, data->id, data->iova_base, data->iova_size); + + pr_info("pgt_base:0x%lx,size:0x%zx, fault_page:0x%lx, rr_page:0x%lx\n", + data->pgt_base, data->pgt_size, data->fault_page, + data->re_route_page); + + if (data->fault_page) { + void *p = __va(data->fault_page); + pr_info("fault_page va: %p", p); + for (i = 0; i < PAGE_SIZE / sizeof(u32); i++) + ((u32 *)p)[i] = 0xDEADDEAD; + } + sprd_iommu_pgt_reset(data); + + dev->pgt = __get_free_pages(GFP_KERNEL, get_order(data->pgt_size)); + if (!dev->pgt) { + pr_err("%s get_free_pages fail\n", data->name); + return -1; + } + + dev->pool = gen_pool_create(12, -1); + if (!dev->pool) { + free_pages((ulong)dev->pgt, get_order(data->pgt_size)); + pr_err("%s gen_pool_create error\n", data->name); + return -1; + } + gen_pool_add(dev->pool, data->iova_base, data->iova_size, -1); + + if (data->re_route_page) { + void *p = __va(data->re_route_page); + pr_info("re_route_page va: %p", p); + for (i = 0; i < PAGE_SIZE / sizeof(u32); i++) + ((u32 *)p)[i] = 0xDEADDEAD; + } + + sprd_iommu_reg_init(data); + + if (data->iommu_rev == 2) { + pr_info("ctrl_reg: 0x%x,cfg_reg: 0x%x,start_mb_addr:0x%x 0x%x", + __raw_readl((void *)(data->ctrl_reg)), + __raw_readl((void *)(data->ctrl_reg + OFFSET_CFG)), + __raw_readl((void *)(data->ctrl_reg + OFFSET_START_MB_ADDR_HI)), + __raw_readl((void *)(data->ctrl_reg + OFFSET_START_MB_ADDR_LO))); + + pr_info("param_reg: 0x%x, rev_reg: 0x%x", + __raw_readl((void *)(data->ctrl_reg + OFFSET_PARAM)), + __raw_readl((void *)(data->ctrl_reg + OFFSET_REV))); + } + + return 0; +} + +int sprd_iommu_exit(struct sprd_iommu_dev *dev) +{ + sprd_iommu_reg_clear(dev->init_data); + gen_pool_destroy(dev->pool); + free_pages(dev->pgt, get_order(dev->init_data->pgt_size)); + dev->pgt = 0; + return 0; +} + +ulong sprd_iommu_iova_alloc(struct sprd_iommu_dev *dev, size_t iova_length) +{ + ulong iova = 0; + + if (0 == iova_length) { + pr_err("%s, %s, iova_length:0x%zx\n", __func__, + dev->init_data->name, iova_length); + return 0; + } + + iova = gen_pool_alloc(dev->pool, iova_length); + pr_debug("%s, %s, iova:0x%lx, iova_length:0x%zx\n", __func__, + dev->init_data->name, iova, iova_length); + if (0 == iova) { + pr_err("%s, %s, iova:0x%lx, iova_length:0x%zx\n", __func__, + dev->init_data->name, iova, iova_length); + } + + return iova; +} + +void sprd_iommu_iova_free(struct sprd_iommu_dev *dev, + ulong iova, size_t iova_length) +{ + struct sprd_iommu_init_data *data = dev->init_data; + + if (((data->iova_base + data->iova_size) < (iova + iova_length)) + || (data->iova_base > iova) || (0 == iova) || (0 == iova_length)) { + pr_err("%s, %s, iova_base:0x%lx, iova_size:0x%zx\n", + __func__, data->name, data->iova_base, data->iova_size); + pr_err("iova:0x%lx, len:0x%zx\n", iova, iova_length); + return; + } + gen_pool_free(dev->pool, iova, iova_length); + pr_debug("%s, %s, iova:0x%lx, len:0x%zx\n", __func__, + data->name, iova, iova_length); +} + +int sprd_iommu_iova_map(struct sprd_iommu_dev *dev, ulong iova, + size_t iova_length, struct sg_table *table) +{ + struct scatterlist *sg; + int i = 0; + ulong iova_cur = 0; + + if ((0 == iova) || (0 == iova_length)) { + pr_err("%s, %s, iova:0x%lx, iova_length:0x%zx\n", __func__, + dev->init_data->name, iova, iova_length); + return -1; + } + iova_cur=iova; + mutex_lock(&dev->mutex_pgt); + /*disable TLB enable in MMU ctrl register*/ + mmu_reg_write(dev->init_data->ctrl_reg,MMU_TLB_EN(0),MMU_TLB_EN_MASK); + for_each_sg(table->sgl, sg, table->nents, i) { + sprd_iommu_pgt_update(dev->init_data, iova_cur, + get_phys_addr(sg), PAGE_ALIGN(sg->length)); + iova_cur += PAGE_ALIGN(sg->length); + if (iova_cur >= iova + iova_length) + break; + } + mmu_reg_write(dev->init_data->ctrl_reg,MMU_TLB_EN(1),MMU_TLB_EN_MASK); + mutex_unlock(&dev->mutex_pgt); + + pr_debug("%s, %s, iova:0x%lx, iova_length:0x%zx\n", __func__, + dev->init_data->name, iova, iova_length); + + return 0; +} + +int sprd_iommu_iova_unmap(struct sprd_iommu_dev *dev, ulong iova, + size_t iova_length) +{ + if ((0 == iova) || (0 == iova_length)) { + pr_err("%s, %s, iova:0x%lx, iova_length:0x%zx\n", __func__, + dev->init_data->name, iova, iova_length); + return -1; + } + + mutex_lock(&dev->mutex_pgt); + /*disable TLB enable in MMU ctrl register*/ + mmu_reg_write(dev->init_data->ctrl_reg,MMU_TLB_EN(0),MMU_TLB_EN_MASK); + sprd_iommu_pgt_clear(dev->init_data, iova, iova_length); + mmu_reg_write(dev->init_data->ctrl_reg,MMU_TLB_EN(1),MMU_TLB_EN_MASK); + mutex_unlock(&dev->mutex_pgt); + + pr_debug("%s, %s, iova:0x%lx, iova_length:0x%zx\n", __func__, + dev->init_data->name, iova, iova_length); + + return 0; +} + +int sprd_iommu_backup(struct sprd_iommu_dev *dev) +{ + mutex_lock(&dev->mutex_pgt); + memcpy((ulong *)dev->pgt, (ulong *)dev->init_data->pgt_base, + dev->init_data->pgt_size); + sprd_iommu_reg_clear(dev->init_data); + mutex_unlock(&dev->mutex_pgt); + + return 0; +} + +int sprd_iommu_restore(struct sprd_iommu_dev *dev) +{ + mutex_lock(&dev->mutex_pgt); + mmu_reg_write(dev->init_data->ctrl_reg, MMU_EN(0), MMU_EN_MASK); + memcpy((ulong *)dev->init_data->pgt_base, (ulong *)dev->pgt, + dev->init_data->pgt_size); + sprd_iommu_reg_init(dev->init_data); + mutex_unlock(&dev->mutex_pgt); + + return 0; +} + +void sprd_iommu_disable(struct sprd_iommu_dev *dev) +{ + mutex_lock(&dev->mutex_pgt); + sprd_iommu_reg_clear(dev->init_data); + mutex_unlock(&dev->mutex_pgt); +} + +void sprd_iommu_enable(struct sprd_iommu_dev *dev) +{ + mutex_lock(&dev->mutex_pgt); + mmu_reg_write(dev->init_data->ctrl_reg, MMU_EN(0), MMU_EN_MASK); + sprd_iommu_reg_init(dev->init_data); + mutex_unlock(&dev->mutex_pgt); +} + +void sprd_iommu_reset_enable(struct sprd_iommu_dev *dev) +{ + mutex_lock(&dev->mutex_pgt); + mmu_reg_write(dev->init_data->ctrl_reg, MMU_EN(0), MMU_EN_MASK); + sprd_iommu_pgt_reset(dev->init_data); + sprd_iommu_reg_init(dev->init_data); + mutex_unlock(&dev->mutex_pgt); +} + +int sprd_iommu_dump(struct sprd_iommu_dev *dev, ulong iova, size_t iova_length) +{ + return 0; +} diff --git a/drivers/gpu/sprd_iommu/sprd_iommu_common.h b/drivers/gpu/sprd_iommu/sprd_iommu_common.h new file mode 100644 index 00000000..12dd0cad --- /dev/null +++ b/drivers/gpu/sprd_iommu/sprd_iommu_common.h @@ -0,0 +1,144 @@ +#ifndef __SPRD_IOMMU_COMMON_H__ +#define __SPRD_IOMMU_COMMON_H__ + +#include <linux/module.h> +#include <linux/file.h> +#include <linux/fs.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/miscdevice.h> +#include <linux/export.h> +#include <linux/slab.h> +#include <linux/uaccess.h> +#include <linux/debugfs.h> +#include <linux/platform_device.h> +#include <linux/genalloc.h> +#include <linux/sprd_iommu.h> +#include <linux/scatterlist.h> +#include <linux/of.h> + +#define OFFSET_CFG 0x4 +#define OFFSET_START_MB_ADDR_LO 0x8 +#define OFFSET_START_MB_ADDR_HI 0xC +#define OFFSET_RR_DEST_LO 0x10 +#define OFFSET_RR_DEST_HI 0x14 +#define OFFSET_OFR_ADDR_LO_R 0x100 +#define OFFSET_OFR_ADDR_HI_R 0x104 +#define OFFSET_TRANS_CNT_R 0x108 +#define OFFSET_PT_MISS_CNT_R 0x10C +#define OFFSET_TLB_MISS_CNT_R 0x110 +#define OFFSET_OFR_ADDR_LO_W 0x114 +#define OFFSET_OFR_ADDR_HI_W 0x118 +#define OFFSET_TRANS_CNT_W 0x11C +#define OFFSET_PT_MISS_CNT_W 0x120 +#define OFFSET_TLB_MISS_CNT_W 0x124 +#define OFFSET_AXI_STAT 0x128 +#define OFFSET_FSM_STAT 0x12C +#define OFFSET_PARAM 0x3F8 +#define OFFSET_REV 0x3FC + +#define AC(X, Y) (X##Y) + +/*common define*/ +/*MMU_CTRL register*/ +#define MMU_EN(_x_) ((AC(_x_, UL)) << 0 & (BIT(0))) +#define MMU_EN_MASK (BIT(0)) +#define MMU_TLB_EN(_x_) ((AC(_x_, UL)) << 1 & (BIT(1))) +#define MMU_TLB_EN_MASK (BIT(1)) + +/*IOMMU rev1 define*/ +#define MMU_V1_RAMCLK_DIV2_EN(_x_) ((AC(_x_, UL)) << 2 & (BIT(2))) +#define MMU_V1_RAMCLK_DIV2_EN_MASK (BIT(2)) +#define MMU_START_MB_ADDR_MASK (BIT(20)|BIT(21)|BIT(22)|BIT(23)|BIT(24)|BIT(25)|BIT(26)|BIT(27)|BIT(28)|BIT(29)|BIT(30)|BIT(31)) + +/*IOMMU rev2 define*/ +/*MMU_CFG register*/ +#define MMU_RAMCLK_DIV2_EN(_x_) ((AC(_x_, UL)) << 0 & (BIT(0))) +#define MMU_RAMCLK_DIV2_EN_MASK (BIT(0)) +#define MMU_TLB_RP_R(_x_) ((AC(_x_, UL)) << 4 & (BIT(4))) +#define MMU_TLB_RP_R_MASK (BIT(4)) +#define MMU_TLB_RP_W(_x_) ((AC(_x_, UL)) << 5 & (BIT(5))) +#define MMU_TLB_RP_W_MASK (BIT(5)) +#define MMU_RR_EN(_x_) ((AC(_x_, UL)) << 6 & (BIT(6))) +#define MMU_RR_EN_MASK (BIT(6)) +#define MMU_MON_EN(_x_) ((AC(_x_, UL)) << 7 & (BIT(7))) +#define MMU_MON_EN_MASK (BIT(7)) + +/*MMU_START_MB_ADDR registers*/ +#define MMU_START_MB_ADDR_LO_MASK (BIT(20)|BIT(21)|BIT(22)|BIT(23)|BIT(24)|BIT(25)|BIT(26)|BIT(27)|BIT(28)|BIT(29)|BIT(30)|BIT(31)) +#define MMU_START_MB_ADDR_HI_MASK (BIT(0)|BIT(1)|BIT(2)|BIT(3)|BIT(4)|BIT(5)|BIT(6)|BIT(7)) + +/*MMU_RR_DEST registers*/ +#define MMU_RR_DEST_LO_MASK (BIT(10)|BIT(11)|BIT(12)|BIT(13)|BIT(14)|BIT(15)|BIT(16)|BIT(17)|BIT(18)|BIT(19)|BIT(20)|BIT(21)|BIT(22)|BIT(23)|BIT(24)|BIT(25)|BIT(26)|BIT(27)|BIT(28)|BIT(29)|BIT(30)|BIT(31)) +#define MMU_RR_DEST_HI_MASK (BIT(0)|BIT(1)|BIT(2)|BIT(3)|BIT(4)|BIT(5)|BIT(6)|BIT(7)) + +/*MMU_OFR_ADDR_R registers*/ +#define MMU_OFR_ADDR_LO_R_MASK (0xFFFFFFFFUL) +#define MMU_OFR_ADDR_HI_R_MASK (BIT(0)|BIT(1)|BIT(2)|BIT(3)|BIT(4)|BIT(5)|BIT(6)|BIT(7)) + +/*MMU CNT_R registers*/ +#define MMU_TRANS_CNT_R_MASK (0xFFFFFFFFUL) +#define MMU_PT_MISS_CNT_R_MASK (0xFFFFFFFFUL) +#define MMU_TLB_MISS_CNT_R_MASK (0xFFFFFFFFUL) + +/*MMU_OFR_ADDR_W register*/ +#define MMU_OFR_ADDR_LO_W_MASK (0xFFFFFFFFUL) +#define MMU_OFR_ADDR_HI_W_MASK (BIT(0)|BIT(1)|BIT(2)|BIT(3)|BIT(4)|BIT(5)|BIT(6)|BIT(7)) + +/*MMU CNT_W registers*/ +#define MMU_TRANS_CNT_W_MASK (0xFFFFFFFFUL) +#define MMU_PT_MISS_CNT_W_MASK (0xFFFFFFFFUL) +#define MMU_TLB_MISS_CNT_W_MASK (0xFFFFFFFFUL) + +/*MMU_PARAM registers*/ +#define MMU_PARAM_AXI_ABW_MASK (BIT(24)|BIT(25)|BIT(26)|BIT(27)|BIT(28)|BIT(29)) +#define MMU_PARAM_TLB_SIZE_MASK (BIT(16)|BIT(17)|BIT(18)|BIT(19)|BIT(20)|BIT(21)|BIT(22)|BIT(23)) +#define MMU_PARAM_PGAE_SIZE_MASK (BIT(8)|BIT(9)|BIT(10)|BIT(11)|BIT(12)) +#define MMU_PARAM_PT_SIZE_MASK (BIT(0)|BIT(1)|BIT(2)|BIT(3)|BIT(4)) + +/*MMU_REV registers*/ +#define MMU_MAJOR_REV_MASK (BIT(4)|BIT(5)|BIT(6)|BIT(7)) +#define MMU_MINOR_REV_MASK (BIT(0)|BIT(1)|BIT(2)|BIT(3)) + + +#define MMU_PAGE_SIZE PAGE_SIZE /*0x1000 need check*/ +/** + * Page table index from address + * Calculates the page table index from the given address +*/ +#define MMU_PTE_OFFSET(address) (((address)>>12) & 0xFFFF) + +/*64-bit virtual addr and 32bit phy addr in arm 64-bit of TSharkL. + *So we use (sizeof(unsigned int)). + */ +#define MMU_IOVA_SIZE_TO_PTE(size) ((sizeof(unsigned int))*(size)/MMU_PAGE_SIZE) + +extern const struct of_device_id iommu_ids[]; + +void mmu_reg_write(unsigned long reg, unsigned long val, unsigned long msk); + +int sprd_iommu_init(struct sprd_iommu_dev *dev, struct sprd_iommu_init_data *data); + +int sprd_iommu_exit(struct sprd_iommu_dev *dev); + +unsigned long sprd_iommu_iova_alloc(struct sprd_iommu_dev *dev, size_t iova_length); + +void sprd_iommu_iova_free(struct sprd_iommu_dev *dev, unsigned long iova, size_t iova_length); + +int sprd_iommu_iova_map(struct sprd_iommu_dev *dev, unsigned long iova, size_t iova_length, struct sg_table *table); + +int sprd_iommu_iova_unmap(struct sprd_iommu_dev *dev, unsigned long iova, size_t iova_length); + +int sprd_iommu_backup(struct sprd_iommu_dev *dev); + +int sprd_iommu_restore(struct sprd_iommu_dev *dev); + +void sprd_iommu_disable(struct sprd_iommu_dev *dev); + +void sprd_iommu_enable(struct sprd_iommu_dev *dev); + +void sprd_iommu_reset_enable(struct sprd_iommu_dev *dev); + +int sprd_iommu_dump(struct sprd_iommu_dev *dev, unsigned long iova, size_t iova_length); + +#endif diff --git a/drivers/gpu/sprd_iommu/sprd_iommu_drv.c b/drivers/gpu/sprd_iommu/sprd_iommu_drv.c new file mode 100644 index 00000000..b4acf820 --- /dev/null +++ b/drivers/gpu/sprd_iommu/sprd_iommu_drv.c @@ -0,0 +1,432 @@ +/* + * drivers/gpu/iommu/iommu.c * + * Copyright (C) 2011 Google, Inc. + * + * 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. + * + */ + +#include <linux/module.h> +#include <linux/file.h> +#include <linux/fs.h> +#include <linux/clk.h> +#include <linux/miscdevice.h> +#include <linux/export.h> +#include <linux/slab.h> +#include <linux/uaccess.h> +#include <linux/debugfs.h> +#include <linux/platform_device.h> +#include <linux/genalloc.h> +#include <linux/sprd_iommu.h> +#include <linux/scatterlist.h> +#include <asm/io.h> +#include "sprd_iommu_sysfs.h" + +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> + +static int sprd_iommu_probe(struct platform_device *pdev); +static int sprd_iommu_remove(struct platform_device *pdev); +static int sprd_iommu_suspend(struct platform_device *pdev, pm_message_t state); +static int sprd_iommu_resume(struct platform_device *pdev); + +struct sprd_iommu_dev *iommu_devs[IOMMU_MAX] = {NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL}; + +static struct sprd_iommu_init_data sprd_iommu_gsp_data = { + .id = IOMMU_GSP, + .name = "sprd_iommu_gsp", +}; + +static struct sprd_iommu_init_data sprd_iommu_mm_data = { + .id = IOMMU_MM, + .name = "sprd_iommu_mm", +}; + +static struct sprd_iommu_init_data sprd_iommu_vsp_data = { + .id = IOMMU_VSP, + .name = "sprd_iommu_vsp", +}; + +static struct sprd_iommu_init_data sprd_iommu_dcam_data = { + .id = IOMMU_DCAM, + .name = "sprd_iommu_dcam", +}; + +static struct sprd_iommu_init_data sprd_iommu_dispc_data = { + .id = IOMMU_DISPC, + .name = "sprd_iommu_dispc", +}; + +static struct sprd_iommu_init_data sprd_iommu_gsp0_data = { + .id = IOMMU_GSP0, + .name = "sprd_iommu_gsp0", +}; + +static struct sprd_iommu_init_data sprd_iommu_gsp1_data = { + .id = IOMMU_GSP1, + .name = "sprd_iommu_gsp1", +}; + +static struct sprd_iommu_init_data sprd_iommu_vpp_data = { + .id = IOMMU_VPP, + .name = "sprd_iommu_vpp", +}; + +const struct of_device_id iommu_ids[] = { + { .compatible = "sprd,sprd_iommu", }, + {}, +}; + +static struct platform_driver iommu_driver = { + .probe = sprd_iommu_probe, + .remove = sprd_iommu_remove, +#ifndef CONFIG_ARCH_SCX35L + .suspend = sprd_iommu_suspend, + .resume = sprd_iommu_resume, +#endif + .driver = { + .owner=THIS_MODULE, + .name="sprd_iommu", + .of_match_table = iommu_ids, + }, +}; + +unsigned long sprd_iova_alloc(int iommu_id, unsigned long iova_length) +{ + if (iommu_devs[iommu_id]) { + return iommu_devs[iommu_id]->ops->iova_alloc( + iommu_devs[iommu_id], iova_length); + } else { + pr_err("%s, id:%d iommu don't exist, error!", + __func__, iommu_id); + return 0; + } +} + +void sprd_iova_free(int iommu_id, unsigned long iova, + unsigned long iova_length) +{ + if (iommu_devs[iommu_id]) { + iommu_devs[iommu_id]->ops->iova_free( + iommu_devs[iommu_id], iova, iova_length); + } else { + pr_err("%s, id:%d iommu don't exist, error!", + __func__, iommu_id); + } +} + +int sprd_iova_map(int iommu_id, unsigned long iova, unsigned long iova_length, + struct sg_table *table) +{ + if (iommu_devs[iommu_id]) { + return iommu_devs[iommu_id]->ops->iova_map( + iommu_devs[iommu_id], iova, iova_length, table); + } else { + pr_err("%s, id:%d iommu don't exist, error!", + __func__, iommu_id); + return -1; + } +} + +int sprd_iova_unmap(int iommu_id, unsigned long iova, + unsigned long iova_length) +{ + if (iommu_devs[iommu_id]) { + return iommu_devs[iommu_id]->ops->iova_unmap( + iommu_devs[iommu_id], iova, iova_length); + } else { + pr_err("%s, id:%d iommu don't exist, error!", + __func__, iommu_id); + return -1; + } +} + +void sprd_iommu_module_enable(uint32_t iommu_id) +{ + if (iommu_devs[iommu_id]->light_sleep_en || + IOMMU_GSP == iommu_id || + IOMMU_DISPC == iommu_id || + IOMMU_GSP0 == iommu_id || + IOMMU_GSP1 == iommu_id) { + if (iommu_devs[iommu_id]) + iommu_devs[iommu_id]->ops->enable(iommu_devs[iommu_id]); + else + pr_err("%s, id:%d iommu don't exist, error!", + __func__, iommu_id); + } +} + +void sprd_iommu_module_disable(uint32_t iommu_id) +{ + if (iommu_devs[iommu_id]->light_sleep_en || + IOMMU_GSP == iommu_id || + IOMMU_DISPC == iommu_id || + IOMMU_GSP0 == iommu_id || + IOMMU_GSP1 == iommu_id) { + if (iommu_devs[iommu_id]) + iommu_devs[iommu_id]->ops->disable(iommu_devs[iommu_id]); + else + pr_err("%s, id:%d iommu don't exist, error!", + __func__, iommu_id); + } +} + +static int sprd_iommu_suspend(struct platform_device *pdev, pm_message_t state) +{ + int err = -1; + struct sprd_iommu_dev *iommu_dev = platform_get_drvdata(pdev); + + pr_info("%s, id:%d, name:%s mmu_clock:0x%p mmu_mclock:0x%p suspend\n", + __func__, iommu_dev->init_data->id, iommu_dev->init_data->name, + iommu_dev->mmu_clock, iommu_dev->mmu_mclock); + err = iommu_dev->ops->backup(iommu_dev); + + return err; +} + +static int sprd_iommu_resume(struct platform_device *pdev) +{ + int err = -1; + struct sprd_iommu_dev *iommu_dev=platform_get_drvdata(pdev); + + pr_info("%s, id:%d, name:%s mmu_clock:0x%p mmu_mclock:0x%p resume\n", + __func__, iommu_dev->init_data->id, iommu_dev->init_data->name, + iommu_dev->mmu_clock, iommu_dev->mmu_mclock); + err = iommu_dev->ops->restore(iommu_dev); + + return err; +} + +static inline int sprd_iommu_get_description(struct device_node *np, + char* desc_name, char**desc) +{ + return of_property_read_string(np, (const char*)desc_name,(const char**) desc); +} + +static int sprd_iommu_get_resource(struct device_node *np, + struct sprd_iommu_init_data *pdata) +{ + int err = 0; + uint32_t val; + struct resource res; + + pr_info("%s,name:%s\n", __func__, pdata->name); + + err = of_address_to_resource(np, 0, &res); + if (err < 0) + return err; + + pdata->iova_base = res.start; + pdata->iova_size = resource_size(&res); + pr_info("%s,iova_base:0x%lx,iova_size:%zx\n", __func__, + pdata->iova_base,pdata->iova_size); + + err = of_address_to_resource(np, 1, &res); + if (err < 0) + return err; + + pr_info("%s,pgt_base phy:0x%x\n", __func__, res.start); + pdata->pgt_base = (unsigned long)ioremap_nocache(res.start, + resource_size(&res)); + if (!pdata->pgt_base) + BUG(); + + pdata->pgt_size = resource_size(&res); + pr_info("%s,pgt_base:%lx,pgt_size:%zx\n", __func__, pdata->pgt_base, + pdata->pgt_size); + + err = of_address_to_resource(np, 2, &res); + if (err < 0) + return err; + + pr_info("%s,ctrl_reg phy:0x%x\n", __func__, res.start); + pdata->ctrl_reg = (unsigned long)ioremap_nocache(res.start, + resource_size(&res)); + if (!pdata->ctrl_reg) + BUG(); + pr_info("%s,ctrl_reg:0x%lx\n", __func__, pdata->ctrl_reg); + + err = of_property_read_u32(np, "sprd,reserved-fault-page", &val); + if (err) { + unsigned long page = 0; + uint32_t i; + + page = __get_free_page(GFP_KERNEL); + if (page) { + pdata->fault_page = virt_to_phys((void *)page); + } else { + pdata->fault_page = 0; + } + pr_info("%s, fault_page: 0x%lx\n", __func__, pdata->fault_page); + } else { + pr_info("%s, reserved fault page phy:0x%x\n", __func__, val); + pdata->fault_page = val; + } + + err = of_property_read_u32(np, "sprd,reserved-rr-page", &val); + if (err) { + pr_info("%s, no reserved rr page addr\n", __func__); + pdata->re_route_page = 0; + } else { + pr_info("%s, reserved rr page phy:0x%x\n", __func__, val); + pdata->re_route_page = val; + } + + err = of_property_read_u32(np, "sprd,iommu-rev", &val); + if (err) { + pr_info("%s, iommu-rev: r1\n", __func__); + pdata->iommu_rev = 1; + } else { + pr_info("%s, iommu-rev: %u\n", __func__, val); + pdata->iommu_rev = val; + } + + return 0; +} + +static int sprd_iommu_probe(struct platform_device *pdev) +{ + char func_name[] = "func-name"; + char func_desc[128]; + char* pdesc=&(func_desc[0]); + struct device_node *np = pdev->dev.of_node; + int err = 0; + struct sprd_iommu_init_data *pdata; + struct sprd_iommu_dev *iommu_dev; + + pr_info("%s, begin\n", __func__); + iommu_dev = kzalloc(sizeof(struct sprd_iommu_dev), GFP_KERNEL); + if (NULL == iommu_dev) { + pr_err("%s, fail to kzalloc\n", __func__); + return -ENOMEM; + } + + if (-1 == sprd_iommu_get_description(np, func_name, &pdesc)) { + pr_err("%s, fail to get description\n", __func__); + err = -EINVAL; + goto errout; + } + pr_info("%s, function name is %s\n", __func__, pdesc); + + if (0 == strncmp("sprd_iommu_gsp", pdesc, 14)) { + pdata = &sprd_iommu_gsp_data; + iommu_dev->ops = &iommu_gsp_ops; + } else if (0 == strncmp("sprd_iommu_mm", pdesc, 13)) { + pdata = &sprd_iommu_mm_data; + iommu_dev->ops = &iommu_mm_ops; + } else if (0 == strncmp("sprd_iommu_vsp", pdesc, 14)) { + pdata = &sprd_iommu_vsp_data; + iommu_dev->ops = &iommu_vsp_ops; + } else if (0 == strncmp("sprd_iommu_dcam", pdesc, 15)) { + pdata = &sprd_iommu_dcam_data; + iommu_dev->ops = &iommu_dcam_ops; + } else if (0 == strncmp("sprd_iommu_dispc", pdesc, 16)) { + pdata = &sprd_iommu_dispc_data; + iommu_dev->ops = &iommu_dispc_ops; + } else if (0 == strncmp("sprd_iommu_gsp0", pdesc, 15)) { + pdata = &sprd_iommu_gsp0_data; + iommu_dev->ops = &iommu_gsp0_ops; + } else if (0 == strncmp("sprd_iommu_gsp1", pdesc, 15)) { + pdata = &sprd_iommu_gsp1_data; + iommu_dev->ops = &iommu_gsp1_ops; + } else if (0 == strncmp("sprd_iommu_vpp", pdesc, 14)) { + pdata = &sprd_iommu_vpp_data; + iommu_dev->ops = &iommu_vpp_ops; + } else { + pr_err("%s, function name mismatch\n", __func__); + err = -EINVAL; + goto errout; + } + + err = sprd_iommu_get_resource(np, pdata); + if (err) { + dev_err(&pdev->dev, "no reg of property specified\n"); + pr_err("%s: failed to get resource!\n", pdesc); + goto errout; + } + + iommu_devs[pdata->id] = iommu_dev; + iommu_dev->init_data = pdata; + mutex_init(&iommu_dev->mutex_pgt); + mutex_init(&iommu_dev->mutex_map); + /*If ddr frequency >= 500Mz, iommu need enable div2 frequency, + * because of limit of iommu iram frq.*/ + iommu_dev->div2_frq = 500; + iommu_dev->light_sleep_en = false; + iommu_dev->misc_dev.minor = MISC_DYNAMIC_MINOR; + iommu_dev->misc_dev.name = pdata->name; + iommu_dev->misc_dev.fops = NULL; + iommu_dev->misc_dev.parent = NULL; + + err = misc_register(&iommu_dev->misc_dev); + if (err) { + pr_err("iommu %s : failed to register misc device %d.\n", + pdata->name, err); + goto errout; + } + + iommu_dev->misc_dev.this_device->of_node = pdev->dev.of_node; + err = iommu_dev->ops->init(iommu_dev, pdata); + if (err) { + pr_err("iommu %s : failed to init device %d.\n", pdata->name, err); + goto errout; + } + + sprd_iommu_sysfs_register(iommu_dev, iommu_dev->init_data->name); + platform_set_drvdata(pdev, iommu_dev); + + pr_info("%s, name:%s, dev->pgt:0x%lx, dev->pool:0x%p mmu_clock:0x%p\n", + __func__, pdata->name, iommu_dev->pgt, + iommu_dev->pool, iommu_dev->mmu_clock); + pr_info("%s, end\n", __func__); + + return 0; + +errout: + iommu_devs[pdata->id] = NULL; + kfree(iommu_dev); + return err; +} + +static int sprd_iommu_remove(struct platform_device *pdev) +{ + struct sprd_iommu_dev *iommu_dev=platform_get_drvdata(pdev); + misc_deregister(&iommu_dev->misc_dev); + sprd_iommu_sysfs_unregister(iommu_dev,iommu_dev->init_data->name); + iommu_dev->ops->exit(iommu_dev); + gen_pool_destroy(iommu_dev->pool); + kfree(iommu_dev); + return 0; +} + +static int __init iommu_init(void) +{ + int err=-1; + pr_info("%s, begin\n", __func__); + err=platform_driver_register(&iommu_driver); + if (err < 0) + pr_err("sprd_iommu register err: %d\n",err); + pr_info("%s, end\n", __func__); + return err; +} + +static void __exit iommu_exit(void) +{ + platform_driver_unregister(&iommu_driver); +} + +module_init(iommu_init); +module_exit(iommu_exit); + +MODULE_DESCRIPTION("SPRD IOMMU Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/sprd_iommu/sprd_iommu_sysfs.c b/drivers/gpu/sprd_iommu/sprd_iommu_sysfs.c new file mode 100644 index 00000000..4bbc3061 --- /dev/null +++ b/drivers/gpu/sprd_iommu/sprd_iommu_sysfs.c @@ -0,0 +1,157 @@ +#include <linux/kernel.h> +#include <linux/fs.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/clk.h> +#include <linux/seq_file.h> +#include <linux/debugfs.h> +#include <asm/uaccess.h> +#include <linux/module.h> +#include <linux/genalloc.h> +#include <linux/sprd_iommu.h> +#include "sprd_iommu_sysfs.h" + +static struct dentry *iommu_debugfs_dir = NULL; +extern int sprd_iommu_gsp_backup(struct sprd_iommu_dev *dev); +extern int sprd_iommu_gsp_restore(struct sprd_iommu_dev *dev); +extern int sprd_iommu_mm_backup(struct sprd_iommu_dev *dev); +extern int sprd_iommu_mm_restore(struct sprd_iommu_dev *dev); + +static void iova_dump_chunk_bitmap(struct gen_pool *pool, + struct gen_pool_chunk *chunk, void *data) +{ + struct seq_file *s = (struct seq_file *)data; + unsigned long nbits = ((chunk->end_addr - chunk->start_addr) + (1UL << 12) - 1) >> 12; + + seq_printf(s,"chunk phys_addr:0x%lx start_addr:0x%lx end_addr:0x%lx\n", + chunk->phys_addr, chunk->start_addr, chunk->end_addr); + seq_bitmap(s, chunk->bits, nbits); + seq_printf(s,"\n"); +} + +static int iova_show(struct seq_file *s, void *unused) +{ + struct sprd_iommu_dev *iommu_dev = (struct sprd_iommu_dev *)s->private; + + seq_printf(s,"iommu_name:%s iova_base:0x%lx iova_size:0x%zx\n", + iommu_dev->init_data->name, iommu_dev->init_data->iova_base, + iommu_dev->init_data->iova_size); + gen_pool_for_each_chunk(iommu_dev->pool, iova_dump_chunk_bitmap, s); + + return 0; +} + +static int iova_open(struct inode *inode, struct file *file) +{ + return single_open(file, iova_show, inode->i_private); +} + +static const struct file_operations iova_fops = { + .owner = THIS_MODULE, + .open = iova_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int pgt_show(struct seq_file *s, void *unused) +{ + struct sprd_iommu_dev *iommu_dev = (struct sprd_iommu_dev *)s->private; + unsigned long i = 0; + + seq_printf(s,"%s pgt_base:0x%lx pgt_size:0x%zx iova_base:0x%lx iova_size:0x%zx\n", + iommu_dev->init_data->name, + iommu_dev->init_data->pgt_base, iommu_dev->init_data->pgt_size, + iommu_dev->init_data->iova_base, iommu_dev->init_data->iova_size); + + mutex_lock(&iommu_dev->mutex_map); + if (iommu_dev->light_sleep_en) { + if (0 == iommu_dev->map_count) { + iommu_dev->ops->enable(iommu_dev); + iommu_dev->ops->open(iommu_dev); + } else { + iommu_dev->ops->enable(iommu_dev); + } + } else { + if (0 == iommu_dev->map_count) { + iommu_dev->ops->enable(iommu_dev); + } + } + + mutex_lock(&iommu_dev->mutex_pgt); + for (i = 0; i < (iommu_dev->init_data->pgt_size >> 2); i++) + { + if(i % 20 == 0) + { + seq_printf(s, "\n0x%lx[0x%lx]: ", + iommu_dev->init_data->pgt_base + i*4, + iommu_dev->init_data->iova_base + i*4096); + } + seq_printf(s, "%x,", + *(((uint32_t *)iommu_dev->init_data->pgt_base) + i)); + } + mutex_unlock(&iommu_dev->mutex_pgt); + + if (iommu_dev->light_sleep_en) { + if (0 == iommu_dev->map_count) { + iommu_dev->ops->release(iommu_dev); + iommu_dev->ops->disable(iommu_dev); + } else { + iommu_dev->ops->disable(iommu_dev); + } + } else { + if (0 == iommu_dev->map_count) { + iommu_dev->ops->disable(iommu_dev); + } + } + mutex_unlock(&iommu_dev->mutex_map); + + return 0; +} + +static int pgt_open(struct inode *inode, struct file *file) +{ + return single_open(file, pgt_show, inode->i_private); +} + +static const struct file_operations pgt_fops = { + .owner = THIS_MODULE, + .open = pgt_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +int sprd_iommu_sysfs_register(struct sprd_iommu_dev *device, + const char *dev_name) +{ + iommu_debugfs_dir = debugfs_create_dir(dev_name, NULL); + + if(ERR_PTR(-ENODEV) == iommu_debugfs_dir) + { + /* Debugfs not supported. */ + iommu_debugfs_dir = NULL; + } + else + { + if(NULL != iommu_debugfs_dir) + { + debugfs_create_file( + "iova", 0444, iommu_debugfs_dir, device, &iova_fops); + debugfs_create_file( + "pgtable", 0444, iommu_debugfs_dir, device, &pgt_fops); + } + } + return 0; +} + +int sprd_iommu_sysfs_unregister(struct sprd_iommu_dev *device, + const char *dev_name) +{ + if(NULL != iommu_debugfs_dir) + { + debugfs_remove_recursive(iommu_debugfs_dir); + } + + return 0; +} diff --git a/drivers/gpu/sprd_iommu/sprd_iommu_sysfs.h b/drivers/gpu/sprd_iommu/sprd_iommu_sysfs.h new file mode 100644 index 00000000..dd8c40bb --- /dev/null +++ b/drivers/gpu/sprd_iommu/sprd_iommu_sysfs.h @@ -0,0 +1,12 @@ +#ifndef __SPRD_IOMMU_SYSFS_H__ +#define __SPRD_IOMMU_SYSFS_H__ + +#include <linux/device.h> + +int sprd_iommu_sysfs_register(struct sprd_iommu_dev *device, + const char *dev_name); + +int sprd_iommu_sysfs_unregister(struct sprd_iommu_dev *device, + const char *dev_name); + +#endif diff --git a/drivers/gpu/sprd_iommu/v2/Makefile b/drivers/gpu/sprd_iommu/v2/Makefile new file mode 100644 index 00000000..ea4985d6 --- /dev/null +++ b/drivers/gpu/sprd_iommu/v2/Makefile @@ -0,0 +1,6 @@ +obj-$(CONFIG_SPRD_IOMMU) += sprd_iommu_vsp.o +obj-$(CONFIG_SPRD_IOMMU) += sprd_iommu_dcam.o +obj-$(CONFIG_SPRD_IOMMU) += sprd_iommu_dispc.o +obj-$(CONFIG_SPRD_IOMMU) += sprd_iommu_gsp0.o +obj-$(CONFIG_SPRD_IOMMU) += sprd_iommu_gsp1.o +obj-$(CONFIG_SPRD_IOMMU) += sprd_iommu_vpp.o diff --git a/drivers/gpu/sprd_iommu/v2/sprd_iommu_dcam.c b/drivers/gpu/sprd_iommu/v2/sprd_iommu_dcam.c new file mode 100644 index 00000000..273c930f --- /dev/null +++ b/drivers/gpu/sprd_iommu/v2/sprd_iommu_dcam.c @@ -0,0 +1,192 @@ +/* + * drivers/gpu/iommu/iommu.c * + * Copyright (C) 2011 Google, Inc. + * + * 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. + * + */ + +#include"../sprd_iommu_common.h" + +void sprd_iommu_dcam_clk_enable(struct sprd_iommu_dev *dev) +{ + pr_info("%s\n", __func__); +#ifndef CONFIG_SC_FPGA + clk_prepare_enable(dev->mmu_mclock); + clk_prepare_enable(dev->mmu_clock); +#endif +} + +void sprd_iommu_dcam_clk_disable(struct sprd_iommu_dev *dev) +{ + pr_info("%s\n", __func__); +#ifndef CONFIG_SC_FPGA + clk_disable_unprepare(dev->mmu_clock); + clk_disable_unprepare(dev->mmu_mclock); +#endif +} + +void sprd_iommu_dcam_open(struct sprd_iommu_dev *dev) +{ +} + +void sprd_iommu_dcam_release(struct sprd_iommu_dev *dev) +{ +} + +int sprd_iommu_dcam_init(struct sprd_iommu_dev *dev, + struct sprd_iommu_init_data *data) +{ + int err = -1; +#ifndef CONFIG_SC_FPGA + struct device_node *np; + + np = dev->misc_dev.this_device->of_node; + if (!np) + return -1; + + dev->mmu_clock = of_clk_get(np, 0); + dev->mmu_mclock = of_clk_get(np, 1); + + if (IS_ERR(dev->mmu_clock) || IS_ERR(dev->mmu_mclock)) { + pr_info("%s, can't get clock:%p, %p\n", __func__, + dev->mmu_clock, dev->mmu_mclock); + goto errorout; + } +#endif + sprd_iommu_dcam_clk_enable(dev); + err = sprd_iommu_init(dev, data); + sprd_iommu_dcam_clk_disable(dev); + + return err; + +#ifndef CONFIG_SC_FPGA +errorout: + if (dev->mmu_clock) + clk_put(dev->mmu_clock); + + if (dev->mmu_mclock) + clk_put(dev->mmu_mclock); + + return -1; +#endif +} + +int sprd_iommu_dcam_exit(struct sprd_iommu_dev *dev) +{ + int err = -1; + + sprd_iommu_dcam_clk_enable(dev); + err = sprd_iommu_exit(dev); + sprd_iommu_dcam_clk_disable(dev); + + return err; +} + +unsigned long sprd_iommu_dcam_iova_alloc(struct sprd_iommu_dev *dev, + size_t iova_length) +{ + return sprd_iommu_iova_alloc(dev, iova_length); +} + +void sprd_iommu_dcam_iova_free(struct sprd_iommu_dev *dev, unsigned long iova, + size_t iova_length) +{ + return sprd_iommu_iova_free(dev, iova, iova_length); +} + +int sprd_iommu_dcam_iova_map(struct sprd_iommu_dev *dev, unsigned long iova, + size_t iova_length, struct sg_table *table) +{ + int err = -1; + + mutex_lock(&dev->mutex_map); + if (0 == dev->map_count) { + sprd_iommu_dcam_clk_enable(dev); + sprd_iommu_reset_enable(dev); + } + dev->map_count++; + err = sprd_iommu_iova_map(dev, iova, iova_length, table); + mutex_unlock(&dev->mutex_map); + + return err; +} + +int sprd_iommu_dcam_iova_unmap(struct sprd_iommu_dev *dev, unsigned long iova, + size_t iova_length) +{ + int err = -1; + + mutex_lock(&dev->mutex_map); + err = sprd_iommu_iova_unmap(dev, iova, iova_length); + dev->map_count--; + if (0 == dev->map_count) { + sprd_iommu_disable(dev); + sprd_iommu_dcam_clk_disable(dev); + } + mutex_unlock(&dev->mutex_map); + + return err; +} + +int sprd_iommu_dcam_backup(struct sprd_iommu_dev *dev) +{ + int err = 0; + + mutex_lock(&dev->mutex_map); + pr_info("%s, map_count:%d\n", __func__, dev->map_count); + + if (dev->map_count > 0) { + err = sprd_iommu_backup(dev); + sprd_iommu_dcam_clk_disable(dev); + } + mutex_unlock(&dev->mutex_map); + + return err; +} + +int sprd_iommu_dcam_restore(struct sprd_iommu_dev *dev) +{ + int err = 0; + + mutex_lock(&dev->mutex_map); + pr_info("%s, map_count:%d\n", __func__, dev->map_count); + + if (dev->map_count > 0) { + sprd_iommu_dcam_clk_enable(dev); + err = sprd_iommu_restore(dev); + } + mutex_unlock(&dev->mutex_map); + + return err; +} + +int sprd_iommu_dcam_dump(struct sprd_iommu_dev *dev, unsigned long iova, + size_t iova_length) +{ + return sprd_iommu_dump(dev, iova, iova_length); +} + +struct sprd_iommu_ops iommu_dcam_ops = { + .init = sprd_iommu_dcam_init, + .exit = sprd_iommu_dcam_exit, + .iova_alloc = sprd_iommu_dcam_iova_alloc, + .iova_free = sprd_iommu_dcam_iova_free, + .iova_map = sprd_iommu_dcam_iova_map, + .iova_unmap = sprd_iommu_dcam_iova_unmap, + .backup = sprd_iommu_dcam_backup, + .restore = sprd_iommu_dcam_restore, + .enable = sprd_iommu_dcam_clk_enable, + .disable = sprd_iommu_dcam_clk_disable, + .dump = sprd_iommu_dcam_dump, + .open = sprd_iommu_dcam_open, + .release = sprd_iommu_dcam_release, +}; + diff --git a/drivers/gpu/sprd_iommu/v2/sprd_iommu_dispc.c b/drivers/gpu/sprd_iommu/v2/sprd_iommu_dispc.c new file mode 100644 index 00000000..ac0ebd6a --- /dev/null +++ b/drivers/gpu/sprd_iommu/v2/sprd_iommu_dispc.c @@ -0,0 +1,229 @@ +/* + * drivers/gpu/iommu/iommu.c * + * Copyright (C) 2011 Google, Inc. + * + * 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. + * + */ + +#include"../sprd_iommu_common.h" + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void sprd_iommu_dispc_early_suspend(struct early_suspend *es) +{ + int err = -1; + + struct sprd_iommu_dev *dev = container_of(es, + struct sprd_iommu_dev, early_suspend); + + mutex_lock(&dev->mutex_map); + pr_info("%s, map_count:%d\n", __func__, dev->map_count); + if (dev->map_count > 0) + err = sprd_iommu_backup(dev); + mutex_unlock(&dev->mutex_map); +} + +static void sprd_iommu_dispc_late_resume(struct early_suspend *es) +{ + int err = -1; + + struct sprd_iommu_dev *dev = container_of(es, + struct sprd_iommu_dev, early_suspend); + + mutex_lock(&dev->mutex_map); + pr_info("%s, map_count:%d\n", __func__, dev->map_count); + if (dev->map_count > 0) + err = sprd_iommu_restore(dev); + mutex_unlock(&dev->mutex_map); +} +#endif + +void sprd_iommu_dispc_clk_enable(struct sprd_iommu_dev *dev) +{ + pr_debug("%s\n", __func__); +#ifndef CONFIG_SC_FPGA + clk_prepare_enable(dev->mmu_mclock); + clk_prepare_enable(dev->mmu_clock); +#endif +} + +void sprd_iommu_dispc_clk_disable(struct sprd_iommu_dev *dev) +{ + pr_debug("%s\n", __func__); +#ifndef CONFIG_SC_FPGA + clk_disable_unprepare(dev->mmu_clock); + clk_disable_unprepare(dev->mmu_mclock); +#endif +} + +void sprd_iommu_dispc_open(struct sprd_iommu_dev *dev) +{ +} + +void sprd_iommu_dispc_release(struct sprd_iommu_dev *dev) +{ +} + +int sprd_iommu_dispc_init(struct sprd_iommu_dev *dev, + struct sprd_iommu_init_data *data) +{ + int err = -1; +#ifndef CONFIG_SC_FPGA + struct device_node *np; + + np = dev->misc_dev.this_device->of_node; + if (!np) + return -1; + + dev->mmu_mclock = of_clk_get(np, 0); + dev->mmu_clock = of_clk_get(np, 2); + + if (IS_ERR(dev->mmu_clock) || IS_ERR(dev->mmu_mclock)) { + pr_info("%s, can't get clock:%p, %p\n", __func__, + dev->mmu_clock, dev->mmu_mclock); + goto errorout; + } +#endif +#ifdef CONFIG_HAS_EARLYSUSPEND + dev->early_suspend.suspend = sprd_iommu_dispc_early_suspend; + dev->early_suspend.resume = sprd_iommu_dispc_late_resume; + dev->early_suspend.level = EARLY_SUSPEND_LEVEL_STOP_DRAWING; + register_early_suspend(&dev->early_suspend); +#endif + + sprd_iommu_dispc_clk_enable(dev); + err = sprd_iommu_init(dev, data); + sprd_iommu_dispc_clk_disable(dev); + + return err; + +#ifndef CONFIG_SC_FPGA +errorout: + if (dev->mmu_clock) + clk_put(dev->mmu_clock); + + if (dev->mmu_mclock) + clk_put(dev->mmu_mclock); + + return -1; +#endif +} + +int sprd_iommu_dispc_exit(struct sprd_iommu_dev *dev) +{ + int err = -1; + + sprd_iommu_dispc_clk_enable(dev); + err = sprd_iommu_exit(dev); + sprd_iommu_dispc_clk_disable(dev); + + return err; +} + +unsigned long sprd_iommu_dispc_iova_alloc(struct sprd_iommu_dev *dev, + size_t iova_length) +{ + return sprd_iommu_iova_alloc(dev, iova_length); +} + +void sprd_iommu_dispc_iova_free(struct sprd_iommu_dev *dev, unsigned long iova, + size_t iova_length) +{ + sprd_iommu_iova_free(dev, iova, iova_length); +} + +int sprd_iommu_dispc_iova_map(struct sprd_iommu_dev *dev, unsigned long iova, + size_t iova_length, struct sg_table *table) +{ + int err = -1; + + mutex_lock(&dev->mutex_map); + if (0 == dev->map_count) + sprd_iommu_enable(dev); + dev->map_count++; + + err = sprd_iommu_iova_map(dev, iova, iova_length, table); + mutex_unlock(&dev->mutex_map); + + return err; +} + +int sprd_iommu_dispc_iova_unmap(struct sprd_iommu_dev *dev, unsigned long iova, + size_t iova_length) +{ + int err = -1; + + mutex_lock(&dev->mutex_map); + err = sprd_iommu_iova_unmap(dev, iova, iova_length); + + dev->map_count--; + if (0 == dev->map_count) + sprd_iommu_disable(dev); + mutex_unlock(&dev->mutex_map); + + return err; +} + +int sprd_iommu_dispc_backup(struct sprd_iommu_dev *dev) +{ +#ifdef CONFIG_HAS_EARLYSUSPEND + return 0; +#else + int err = -1; + + mutex_lock(&dev->mutex_map); + pr_info("%s, map_count:%d\n", __func__, dev->map_count); + if (dev->map_count > 0) + err = sprd_iommu_backup(dev); + mutex_unlock(&dev->mutex_map); + + return err; +#endif +} + +int sprd_iommu_dispc_restore(struct sprd_iommu_dev *dev) +{ +#ifdef CONFIG_HAS_EARLYSUSPEND + return 0; +#else + int err = -1; + + mutex_lock(&dev->mutex_map); + pr_info("%s, map_count:%d\n", __func__, dev->map_count); + if (dev->map_count > 0) + err = sprd_iommu_restore(dev); + mutex_unlock(&dev->mutex_map); + + return 0; +#endif +} + +int sprd_iommu_dispc_dump(struct sprd_iommu_dev *dev, unsigned long iova, + size_t iova_length) +{ + return sprd_iommu_dump(dev, iova, iova_length); +} + +struct sprd_iommu_ops iommu_dispc_ops = { + .init = sprd_iommu_dispc_init, + .exit = sprd_iommu_dispc_exit, + .iova_alloc = sprd_iommu_dispc_iova_alloc, + .iova_free = sprd_iommu_dispc_iova_free, + .iova_map = sprd_iommu_dispc_iova_map, + .iova_unmap = sprd_iommu_dispc_iova_unmap, + .backup = sprd_iommu_dispc_backup, + .restore = sprd_iommu_dispc_restore, + .enable = sprd_iommu_dispc_clk_enable, + .disable = sprd_iommu_dispc_clk_disable, + .dump = sprd_iommu_dispc_dump, + .open = sprd_iommu_dispc_open, + .release = sprd_iommu_dispc_release, +}; + diff --git a/drivers/gpu/sprd_iommu/v2/sprd_iommu_gsp0.c b/drivers/gpu/sprd_iommu/v2/sprd_iommu_gsp0.c new file mode 100644 index 00000000..27a37502 --- /dev/null +++ b/drivers/gpu/sprd_iommu/v2/sprd_iommu_gsp0.c @@ -0,0 +1,229 @@ +/* + * drivers/gpu/iommu/iommu.c * + * Copyright (C) 2011 Google, Inc. + * + * 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. + * + */ + +#include"../sprd_iommu_common.h" + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void sprd_iommu_gsp0_early_suspend(struct early_suspend *es) +{ + int err = -1; + + struct sprd_iommu_dev *dev = container_of(es, + struct sprd_iommu_dev, early_suspend); + + mutex_lock(&dev->mutex_map); + pr_info("%s, map_count:%d\n", __func__, dev->map_count); + if (dev->map_count > 0) + err = sprd_iommu_backup(dev); + mutex_unlock(&dev->mutex_map); +} + +static void sprd_iommu_gsp0_late_resume(struct early_suspend *es) +{ + int err = -1; + + struct sprd_iommu_dev *dev = container_of(es, + struct sprd_iommu_dev, early_suspend); + + mutex_lock(&dev->mutex_map); + pr_info("%s, map_count:%d\n", __func__, dev->map_count); + if (dev->map_count > 0) + err = sprd_iommu_restore(dev); + mutex_unlock(&dev->mutex_map); +} +#endif + +void sprd_iommu_gsp0_clk_enable(struct sprd_iommu_dev *dev) +{ + pr_debug("%s\n", __func__); +#ifndef CONFIG_SC_FPGA + clk_prepare_enable(dev->mmu_mclock); + clk_prepare_enable(dev->mmu_clock); +#endif +} + +void sprd_iommu_gsp0_clk_disable(struct sprd_iommu_dev *dev) +{ + pr_debug("%s\n", __func__); +#ifndef CONFIG_SC_FPGA + clk_disable_unprepare(dev->mmu_clock); + clk_disable_unprepare(dev->mmu_mclock); +#endif +} + +void sprd_iommu_gsp0_open(struct sprd_iommu_dev *dev) +{ +} + +void sprd_iommu_gsp0_release(struct sprd_iommu_dev *dev) +{ +} + +int sprd_iommu_gsp0_init(struct sprd_iommu_dev *dev, + struct sprd_iommu_init_data *data) +{ + int err = -1; +#ifndef CONFIG_SC_FPGA + struct device_node *np; + + np = dev->misc_dev.this_device->of_node; + if (!np) + return -1; + + dev->mmu_mclock = of_clk_get(np, 0); + dev->mmu_clock = of_clk_get(np, 2); + + if (IS_ERR(dev->mmu_clock) || IS_ERR(dev->mmu_mclock)) { + pr_info("%s, can't get clock:%p, %p\n", __func__, + dev->mmu_clock, dev->mmu_mclock); + goto errorout; + } +#endif +#ifdef CONFIG_HAS_EARLYSUSPEND + dev->early_suspend.suspend = sprd_iommu_gsp0_early_suspend; + dev->early_suspend.resume = sprd_iommu_gsp0_late_resume; + dev->early_suspend.level = EARLY_SUSPEND_LEVEL_STOP_DRAWING; + register_early_suspend(&dev->early_suspend); +#endif + + sprd_iommu_gsp0_clk_enable(dev); + err = sprd_iommu_init(dev, data); + sprd_iommu_gsp0_clk_disable(dev); + + return err; + +#ifndef CONFIG_SC_FPGA +errorout: + if (dev->mmu_clock) + clk_put(dev->mmu_clock); + + if (dev->mmu_mclock) + clk_put(dev->mmu_mclock); + + return -1; +#endif +} + +int sprd_iommu_gsp0_exit(struct sprd_iommu_dev *dev) +{ + int err = -1; + + sprd_iommu_gsp0_clk_enable(dev); + err = sprd_iommu_exit(dev); + sprd_iommu_gsp0_clk_disable(dev); + + return err; +} + +unsigned long sprd_iommu_gsp0_iova_alloc(struct sprd_iommu_dev *dev, + size_t iova_length) +{ + return sprd_iommu_iova_alloc(dev, iova_length); +} + +void sprd_iommu_gsp0_iova_free(struct sprd_iommu_dev *dev, unsigned long iova, + size_t iova_length) +{ + sprd_iommu_iova_free(dev, iova, iova_length); +} + +int sprd_iommu_gsp0_iova_map(struct sprd_iommu_dev *dev, unsigned long iova, + size_t iova_length, struct sg_table *table) +{ + int err = -1; + + mutex_lock(&dev->mutex_map); + if (0 == dev->map_count) + sprd_iommu_enable(dev); + dev->map_count++; + + err = sprd_iommu_iova_map(dev, iova, iova_length, table); + mutex_unlock(&dev->mutex_map); + + return err; +} + +int sprd_iommu_gsp0_iova_unmap(struct sprd_iommu_dev *dev, unsigned long iova, + size_t iova_length) +{ + int err = -1; + + mutex_lock(&dev->mutex_map); + err = sprd_iommu_iova_unmap(dev, iova, iova_length); + + dev->map_count--; + if (0 == dev->map_count) + sprd_iommu_disable(dev); + mutex_unlock(&dev->mutex_map); + + return err; +} + +int sprd_iommu_gsp0_backup(struct sprd_iommu_dev *dev) +{ +#ifdef CONFIG_HAS_EARLYSUSPEND + return 0; +#else + int err = -1; + + mutex_lock(&dev->mutex_map); + pr_info("%s, map_count:%d\n", __func__, dev->map_count); + if (dev->map_count > 0) + err = sprd_iommu_backup(dev); + mutex_unlock(&dev->mutex_map); + + return err; +#endif +} + +int sprd_iommu_gsp0_restore(struct sprd_iommu_dev *dev) +{ +#ifdef CONFIG_HAS_EARLYSUSPEND + return 0; +#else + int err = -1; + + mutex_lock(&dev->mutex_map); + pr_info("%s, map_count:%d\n", __func__, dev->map_count); + if (dev->map_count > 0) + err = sprd_iommu_restore(dev); + mutex_unlock(&dev->mutex_map); + + return 0; +#endif +} + +int sprd_iommu_gsp0_dump(struct sprd_iommu_dev *dev, unsigned long iova, + size_t iova_length) +{ + return sprd_iommu_dump(dev, iova, iova_length); +} + +struct sprd_iommu_ops iommu_gsp0_ops = { + .init = sprd_iommu_gsp0_init, + .exit = sprd_iommu_gsp0_exit, + .iova_alloc = sprd_iommu_gsp0_iova_alloc, + .iova_free = sprd_iommu_gsp0_iova_free, + .iova_map = sprd_iommu_gsp0_iova_map, + .iova_unmap = sprd_iommu_gsp0_iova_unmap, + .backup = sprd_iommu_gsp0_backup, + .restore = sprd_iommu_gsp0_restore, + .enable = sprd_iommu_gsp0_clk_enable, + .disable = sprd_iommu_gsp0_clk_disable, + .dump = sprd_iommu_gsp0_dump, + .open = sprd_iommu_gsp0_open, + .release = sprd_iommu_gsp0_release, +}; + diff --git a/drivers/gpu/sprd_iommu/v2/sprd_iommu_gsp1.c b/drivers/gpu/sprd_iommu/v2/sprd_iommu_gsp1.c new file mode 100644 index 00000000..3e3fb359 --- /dev/null +++ b/drivers/gpu/sprd_iommu/v2/sprd_iommu_gsp1.c @@ -0,0 +1,229 @@ +/* + * drivers/gpu/iommu/iommu.c * + * Copyright (C) 2011 Google, Inc. + * + * 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. + * + */ + +#include"../sprd_iommu_common.h" + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void sprd_iommu_gsp1_early_suspend(struct early_suspend *es) +{ + int err = -1; + + struct sprd_iommu_dev *dev = container_of(es, + struct sprd_iommu_dev, early_suspend); + + mutex_lock(&dev->mutex_map); + pr_info("%s, map_count:%d\n", __func__, dev->map_count); + if (dev->map_count > 0) + err = sprd_iommu_backup(dev); + mutex_unlock(&dev->mutex_map); +} + +static void sprd_iommu_gsp1_late_resume(struct early_suspend *es) +{ + int err = -1; + + struct sprd_iommu_dev *dev = container_of(es, + struct sprd_iommu_dev, early_suspend); + + mutex_lock(&dev->mutex_map); + pr_info("%s, map_count:%d\n", __func__, dev->map_count); + if (dev->map_count > 0) + err = sprd_iommu_restore(dev); + mutex_unlock(&dev->mutex_map); +} +#endif + +void sprd_iommu_gsp1_clk_enable(struct sprd_iommu_dev *dev) +{ + pr_debug("%s\n", __func__); +#ifndef CONFIG_SC_FPGA + clk_prepare_enable(dev->mmu_mclock); + clk_prepare_enable(dev->mmu_clock); +#endif +} + +void sprd_iommu_gsp1_clk_disable(struct sprd_iommu_dev *dev) +{ + pr_debug("%s\n", __func__); +#ifndef CONFIG_SC_FPGA + clk_disable_unprepare(dev->mmu_clock); + clk_disable_unprepare(dev->mmu_mclock); +#endif +} + +void sprd_iommu_gsp1_open(struct sprd_iommu_dev *dev) +{ +} + +void sprd_iommu_gsp1_release(struct sprd_iommu_dev *dev) +{ +} + +int sprd_iommu_gsp1_init(struct sprd_iommu_dev *dev, + struct sprd_iommu_init_data *data) +{ + int err = -1; +#ifndef CONFIG_SC_FPGA + struct device_node *np; + + np = dev->misc_dev.this_device->of_node; + if (!np) + return -1; + + dev->mmu_mclock = of_clk_get(np, 0); + dev->mmu_clock = of_clk_get(np, 2); + + if (IS_ERR(dev->mmu_clock) || IS_ERR(dev->mmu_mclock)) { + pr_info("%s, can't get clock:%p, %p\n", __func__, + dev->mmu_clock, dev->mmu_mclock); + goto errorout; + } +#endif +#ifdef CONFIG_HAS_EARLYSUSPEND + dev->early_suspend.suspend = sprd_iommu_gsp1_early_suspend; + dev->early_suspend.resume = sprd_iommu_gsp1_late_resume; + dev->early_suspend.level = EARLY_SUSPEND_LEVEL_STOP_DRAWING; + register_early_suspend(&dev->early_suspend); +#endif + + sprd_iommu_gsp1_clk_enable(dev); + err = sprd_iommu_init(dev, data); + sprd_iommu_gsp1_clk_disable(dev); + + return err; + +#ifndef CONFIG_SC_FPGA +errorout: + if (dev->mmu_clock) + clk_put(dev->mmu_clock); + + if (dev->mmu_mclock) + clk_put(dev->mmu_mclock); + + return -1; +#endif +} + +int sprd_iommu_gsp1_exit(struct sprd_iommu_dev *dev) +{ + int err = -1; + + sprd_iommu_gsp1_clk_enable(dev); + err = sprd_iommu_exit(dev); + sprd_iommu_gsp1_clk_disable(dev); + + return err; +} + +unsigned long sprd_iommu_gsp1_iova_alloc(struct sprd_iommu_dev *dev, + size_t iova_length) +{ + return sprd_iommu_iova_alloc(dev, iova_length); +} + +void sprd_iommu_gsp1_iova_free(struct sprd_iommu_dev *dev, unsigned long iova, + size_t iova_length) +{ + sprd_iommu_iova_free(dev, iova, iova_length); +} + +int sprd_iommu_gsp1_iova_map(struct sprd_iommu_dev *dev, unsigned long iova, + size_t iova_length, struct sg_table *table) +{ + int err = -1; + + mutex_lock(&dev->mutex_map); + if (0 == dev->map_count) + sprd_iommu_enable(dev); + dev->map_count++; + + err = sprd_iommu_iova_map(dev, iova, iova_length, table); + mutex_unlock(&dev->mutex_map); + + return err; +} + +int sprd_iommu_gsp1_iova_unmap(struct sprd_iommu_dev *dev, unsigned long iova, + size_t iova_length) +{ + int err = -1; + + mutex_lock(&dev->mutex_map); + err = sprd_iommu_iova_unmap(dev, iova, iova_length); + + dev->map_count--; + if (0 == dev->map_count) + sprd_iommu_disable(dev); + mutex_unlock(&dev->mutex_map); + + return err; +} + +int sprd_iommu_gsp1_backup(struct sprd_iommu_dev *dev) +{ +#ifdef CONFIG_HAS_EARLYSUSPEND + return 0; +#else + int err = -1; + + mutex_lock(&dev->mutex_map); + pr_info("%s, map_count:%d\n", __func__, dev->map_count); + if (dev->map_count > 0) + err = sprd_iommu_backup(dev); + mutex_unlock(&dev->mutex_map); + + return err; +#endif +} + +int sprd_iommu_gsp1_restore(struct sprd_iommu_dev *dev) +{ +#ifdef CONFIG_HAS_EARLYSUSPEND + return 0; +#else + int err = -1; + + mutex_lock(&dev->mutex_map); + pr_info("%s, map_count:%d\n", __func__, dev->map_count); + if (dev->map_count > 0) + err = sprd_iommu_restore(dev); + mutex_unlock(&dev->mutex_map); + + return 0; +#endif +} + +int sprd_iommu_gsp1_dump(struct sprd_iommu_dev *dev, unsigned long iova, + size_t iova_length) +{ + return sprd_iommu_dump(dev, iova, iova_length); +} + +struct sprd_iommu_ops iommu_gsp1_ops = { + .init = sprd_iommu_gsp1_init, + .exit = sprd_iommu_gsp1_exit, + .iova_alloc = sprd_iommu_gsp1_iova_alloc, + .iova_free = sprd_iommu_gsp1_iova_free, + .iova_map = sprd_iommu_gsp1_iova_map, + .iova_unmap = sprd_iommu_gsp1_iova_unmap, + .backup = sprd_iommu_gsp1_backup, + .restore = sprd_iommu_gsp1_restore, + .enable = sprd_iommu_gsp1_clk_enable, + .disable = sprd_iommu_gsp1_clk_disable, + .dump = sprd_iommu_gsp1_dump, + .open = sprd_iommu_gsp1_open, + .release = sprd_iommu_gsp1_release, +}; + diff --git a/drivers/gpu/sprd_iommu/v2/sprd_iommu_vpp.c b/drivers/gpu/sprd_iommu/v2/sprd_iommu_vpp.c new file mode 100644 index 00000000..ba377e2f --- /dev/null +++ b/drivers/gpu/sprd_iommu/v2/sprd_iommu_vpp.c @@ -0,0 +1,192 @@ +/* + * drivers/gpu/iommu/iommu.c * + * Copyright (C) 2011 Google, Inc. + * + * 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. + * + */ + +#include"../sprd_iommu_common.h" + +void sprd_iommu_vpp_clk_enable(struct sprd_iommu_dev *dev) +{ + pr_info("%s\n", __func__); +#ifndef CONFIG_SC_FPGA + clk_prepare_enable(dev->mmu_mclock); + clk_prepare_enable(dev->mmu_clock); +#endif +} + +void sprd_iommu_vpp_clk_disable(struct sprd_iommu_dev *dev) +{ + pr_info("%s\n", __func__); +#ifndef CONFIG_SC_FPGA + clk_disable_unprepare(dev->mmu_clock); + clk_disable_unprepare(dev->mmu_mclock); +#endif +} + +void sprd_iommu_vpp_open(struct sprd_iommu_dev *dev) +{ +} + +void sprd_iommu_vpp_release(struct sprd_iommu_dev *dev) +{ +} + +int sprd_iommu_vpp_init(struct sprd_iommu_dev *dev, + struct sprd_iommu_init_data *data) +{ + int err = -1; +#ifndef CONFIG_SC_FPGA + struct device_node *np; + + np = dev->misc_dev.this_device->of_node; + if (!np) + return -1; + + dev->mmu_clock = of_clk_get(np, 0); + dev->mmu_mclock = of_clk_get(np, 1); + + if (IS_ERR(dev->mmu_clock) || IS_ERR(dev->mmu_mclock)) { + pr_info("%s, can't get clock:%p, %p\n", __func__, + dev->mmu_clock, dev->mmu_mclock); + goto errorout; + } +#endif + sprd_iommu_vpp_clk_enable(dev); + err = sprd_iommu_init(dev, data); + sprd_iommu_vpp_clk_disable(dev); + + return err; + +#ifndef CONFIG_SC_FPGA +errorout: + if (dev->mmu_clock) + clk_put(dev->mmu_clock); + + if (dev->mmu_mclock) + clk_put(dev->mmu_mclock); + + return -1; +#endif +} + +int sprd_iommu_vpp_exit(struct sprd_iommu_dev *dev) +{ + int err = -1; + + sprd_iommu_vpp_clk_enable(dev); + err = sprd_iommu_exit(dev); + sprd_iommu_vpp_clk_disable(dev); + + return err; +} + +unsigned long sprd_iommu_vpp_iova_alloc(struct sprd_iommu_dev *dev, + size_t iova_length) +{ + return sprd_iommu_iova_alloc(dev, iova_length); +} + +void sprd_iommu_vpp_iova_free(struct sprd_iommu_dev *dev, unsigned long iova, + size_t iova_length) +{ + return sprd_iommu_iova_free(dev, iova, iova_length); +} + +int sprd_iommu_vpp_iova_map(struct sprd_iommu_dev *dev, unsigned long iova, + size_t iova_length, struct sg_table *table) +{ + int err = -1; + + mutex_lock(&dev->mutex_map); + if (0 == dev->map_count) { + sprd_iommu_vpp_clk_enable(dev); + sprd_iommu_reset_enable(dev); + } + dev->map_count++; + err = sprd_iommu_iova_map(dev, iova, iova_length, table); + mutex_unlock(&dev->mutex_map); + + return err; +} + +int sprd_iommu_vpp_iova_unmap(struct sprd_iommu_dev *dev, unsigned long iova, + size_t iova_length) +{ + int err = -1; + + mutex_lock(&dev->mutex_map); + err = sprd_iommu_iova_unmap(dev, iova, iova_length); + dev->map_count--; + if (0 == dev->map_count) { + sprd_iommu_disable(dev); + sprd_iommu_vpp_clk_disable(dev); + } + mutex_unlock(&dev->mutex_map); + + return err; +} + +int sprd_iommu_vpp_backup(struct sprd_iommu_dev *dev) +{ + int err = 0; + + mutex_lock(&dev->mutex_map); + pr_info("%s, map_count:%d\n", __func__, dev->map_count); + + if (dev->map_count > 0) { + err = sprd_iommu_backup(dev); + sprd_iommu_vpp_clk_disable(dev); + } + mutex_unlock(&dev->mutex_map); + + return err; +} + +int sprd_iommu_vpp_restore(struct sprd_iommu_dev *dev) +{ + int err = 0; + + mutex_lock(&dev->mutex_map); + pr_info("%s, map_count:%d\n", __func__, dev->map_count); + + if (dev->map_count > 0) { + sprd_iommu_vpp_clk_enable(dev); + err = sprd_iommu_restore(dev); + } + mutex_unlock(&dev->mutex_map); + + return err; +} + +int sprd_iommu_vpp_dump(struct sprd_iommu_dev *dev, unsigned long iova, + size_t iova_length) +{ + return sprd_iommu_dump(dev, iova, iova_length); +} + +struct sprd_iommu_ops iommu_vpp_ops = { + .init = sprd_iommu_vpp_init, + .exit = sprd_iommu_vpp_exit, + .iova_alloc = sprd_iommu_vpp_iova_alloc, + .iova_free = sprd_iommu_vpp_iova_free, + .iova_map = sprd_iommu_vpp_iova_map, + .iova_unmap = sprd_iommu_vpp_iova_unmap, + .backup = sprd_iommu_vpp_backup, + .restore = sprd_iommu_vpp_restore, + .enable = sprd_iommu_vpp_clk_enable, + .disable = sprd_iommu_vpp_clk_disable, + .dump = sprd_iommu_vpp_dump, + .open = sprd_iommu_vpp_open, + .release = sprd_iommu_vpp_release, +}; + diff --git a/drivers/gpu/sprd_iommu/v2/sprd_iommu_vsp.c b/drivers/gpu/sprd_iommu/v2/sprd_iommu_vsp.c new file mode 100644 index 00000000..2f1a28b9 --- /dev/null +++ b/drivers/gpu/sprd_iommu/v2/sprd_iommu_vsp.c @@ -0,0 +1,192 @@ +/* + * drivers/gpu/iommu/iommu.c * + * Copyright (C) 2011 Google, Inc. + * + * 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. + * + */ + +#include"../sprd_iommu_common.h" + +void sprd_iommu_vsp_clk_enable(struct sprd_iommu_dev *dev) +{ + pr_info("%s\n", __func__); +#ifndef CONFIG_SC_FPGA + clk_prepare_enable(dev->mmu_mclock); + clk_prepare_enable(dev->mmu_clock); +#endif +} + +void sprd_iommu_vsp_clk_disable(struct sprd_iommu_dev *dev) +{ + pr_info("%s\n", __func__); +#ifndef CONFIG_SC_FPGA + clk_disable_unprepare(dev->mmu_clock); + clk_disable_unprepare(dev->mmu_mclock); +#endif +} + +void sprd_iommu_vsp_open(struct sprd_iommu_dev *dev) +{ +} + +void sprd_iommu_vsp_release(struct sprd_iommu_dev *dev) +{ +} + +int sprd_iommu_vsp_init(struct sprd_iommu_dev *dev, + struct sprd_iommu_init_data *data) +{ + int err = -1; +#ifndef CONFIG_SC_FPGA + struct device_node *np; + + np = dev->misc_dev.this_device->of_node; + if (!np) + return -1; + + dev->mmu_clock = of_clk_get(np, 0); + dev->mmu_mclock = of_clk_get(np, 1); + + if (IS_ERR(dev->mmu_clock) || IS_ERR(dev->mmu_mclock)) { + pr_info("%s, can't get clock:%p, %p\n", __func__, + dev->mmu_clock, dev->mmu_mclock); + goto errorout; + } +#endif + sprd_iommu_vsp_clk_enable(dev); + err = sprd_iommu_init(dev, data); + sprd_iommu_vsp_clk_disable(dev); + + return err; + +#ifndef CONFIG_SC_FPGA +errorout: + if (dev->mmu_clock) + clk_put(dev->mmu_clock); + + if (dev->mmu_mclock) + clk_put(dev->mmu_mclock); + + return -1; +#endif +} + +int sprd_iommu_vsp_exit(struct sprd_iommu_dev *dev) +{ + int err = -1; + + sprd_iommu_vsp_clk_enable(dev); + err = sprd_iommu_exit(dev); + sprd_iommu_vsp_clk_disable(dev); + + return err; +} + +unsigned long sprd_iommu_vsp_iova_alloc(struct sprd_iommu_dev *dev, + size_t iova_length) +{ + return sprd_iommu_iova_alloc(dev, iova_length); +} + +void sprd_iommu_vsp_iova_free(struct sprd_iommu_dev *dev, unsigned long iova, + size_t iova_length) +{ + return sprd_iommu_iova_free(dev, iova, iova_length); +} + +int sprd_iommu_vsp_iova_map(struct sprd_iommu_dev *dev, unsigned long iova, + size_t iova_length, struct sg_table *table) +{ + int err = -1; + + mutex_lock(&dev->mutex_map); + if (0 == dev->map_count) { + sprd_iommu_vsp_clk_enable(dev); + sprd_iommu_reset_enable(dev); + } + dev->map_count++; + err = sprd_iommu_iova_map(dev, iova, iova_length, table); + mutex_unlock(&dev->mutex_map); + + return err; +} + +int sprd_iommu_vsp_iova_unmap(struct sprd_iommu_dev *dev, unsigned long iova, + size_t iova_length) +{ + int err = -1; + + mutex_lock(&dev->mutex_map); + err = sprd_iommu_iova_unmap(dev, iova, iova_length); + dev->map_count--; + if (0 == dev->map_count) { + sprd_iommu_disable(dev); + sprd_iommu_vsp_clk_disable(dev); + } + mutex_unlock(&dev->mutex_map); + + return err; +} + +int sprd_iommu_vsp_backup(struct sprd_iommu_dev *dev) +{ + int err = 0; + + mutex_lock(&dev->mutex_map); + pr_info("%s, map_count:%d\n", __func__, dev->map_count); + + if (dev->map_count > 0) { + err = sprd_iommu_backup(dev); + sprd_iommu_vsp_clk_disable(dev); + } + mutex_unlock(&dev->mutex_map); + + return err; +} + +int sprd_iommu_vsp_restore(struct sprd_iommu_dev *dev) +{ + int err = 0; + + mutex_lock(&dev->mutex_map); + pr_info("%s, map_count:%d\n", __func__, dev->map_count); + + if (dev->map_count > 0) { + sprd_iommu_vsp_clk_enable(dev); + err = sprd_iommu_restore(dev); + } + mutex_unlock(&dev->mutex_map); + + return err; +} + +int sprd_iommu_vsp_dump(struct sprd_iommu_dev *dev, unsigned long iova, + size_t iova_length) +{ + return sprd_iommu_dump(dev, iova, iova_length); +} + +struct sprd_iommu_ops iommu_vsp_ops = { + .init = sprd_iommu_vsp_init, + .exit = sprd_iommu_vsp_exit, + .iova_alloc = sprd_iommu_vsp_iova_alloc, + .iova_free = sprd_iommu_vsp_iova_free, + .iova_map = sprd_iommu_vsp_iova_map, + .iova_unmap = sprd_iommu_vsp_iova_unmap, + .backup = sprd_iommu_vsp_backup, + .restore = sprd_iommu_vsp_restore, + .enable = sprd_iommu_vsp_clk_enable, + .disable = sprd_iommu_vsp_clk_disable, + .dump = sprd_iommu_vsp_dump, + .open = sprd_iommu_vsp_open, + .release = sprd_iommu_vsp_release, +}; + |
