18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * IOMMU API for Graphics Address Relocation Table on Tegra20 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2010-2012, NVIDIA CORPORATION. All rights reserved. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author: Hiroshi DOYU <hdoyu@nvidia.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#define dev_fmt(fmt) "gart: " fmt 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/io.h> 138c2ecf20Sopenharmony_ci#include <linux/iommu.h> 148c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 158c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 188c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <soc/tegra/mc.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define GART_REG_BASE 0x24 238c2ecf20Sopenharmony_ci#define GART_CONFIG (0x24 - GART_REG_BASE) 248c2ecf20Sopenharmony_ci#define GART_ENTRY_ADDR (0x28 - GART_REG_BASE) 258c2ecf20Sopenharmony_ci#define GART_ENTRY_DATA (0x2c - GART_REG_BASE) 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define GART_ENTRY_PHYS_ADDR_VALID BIT(31) 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define GART_PAGE_SHIFT 12 308c2ecf20Sopenharmony_ci#define GART_PAGE_SIZE (1 << GART_PAGE_SHIFT) 318c2ecf20Sopenharmony_ci#define GART_PAGE_MASK GENMASK(30, GART_PAGE_SHIFT) 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci/* bitmap of the page sizes currently supported */ 348c2ecf20Sopenharmony_ci#define GART_IOMMU_PGSIZES (GART_PAGE_SIZE) 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistruct gart_device { 378c2ecf20Sopenharmony_ci void __iomem *regs; 388c2ecf20Sopenharmony_ci u32 *savedata; 398c2ecf20Sopenharmony_ci unsigned long iovmm_base; /* offset to vmm_area start */ 408c2ecf20Sopenharmony_ci unsigned long iovmm_end; /* offset to vmm_area end */ 418c2ecf20Sopenharmony_ci spinlock_t pte_lock; /* for pagetable */ 428c2ecf20Sopenharmony_ci spinlock_t dom_lock; /* for active domain */ 438c2ecf20Sopenharmony_ci unsigned int active_devices; /* number of active devices */ 448c2ecf20Sopenharmony_ci struct iommu_domain *active_domain; /* current active domain */ 458c2ecf20Sopenharmony_ci struct iommu_device iommu; /* IOMMU Core handle */ 468c2ecf20Sopenharmony_ci struct device *dev; 478c2ecf20Sopenharmony_ci}; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic struct gart_device *gart_handle; /* unique for a system */ 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic bool gart_debug; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci/* 548c2ecf20Sopenharmony_ci * Any interaction between any block on PPSB and a block on APB or AHB 558c2ecf20Sopenharmony_ci * must have these read-back to ensure the APB/AHB bus transaction is 568c2ecf20Sopenharmony_ci * complete before initiating activity on the PPSB block. 578c2ecf20Sopenharmony_ci */ 588c2ecf20Sopenharmony_ci#define FLUSH_GART_REGS(gart) readl_relaxed((gart)->regs + GART_CONFIG) 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci#define for_each_gart_pte(gart, iova) \ 618c2ecf20Sopenharmony_ci for (iova = gart->iovmm_base; \ 628c2ecf20Sopenharmony_ci iova < gart->iovmm_end; \ 638c2ecf20Sopenharmony_ci iova += GART_PAGE_SIZE) 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic inline void gart_set_pte(struct gart_device *gart, 668c2ecf20Sopenharmony_ci unsigned long iova, unsigned long pte) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci writel_relaxed(iova, gart->regs + GART_ENTRY_ADDR); 698c2ecf20Sopenharmony_ci writel_relaxed(pte, gart->regs + GART_ENTRY_DATA); 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic inline unsigned long gart_read_pte(struct gart_device *gart, 738c2ecf20Sopenharmony_ci unsigned long iova) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci unsigned long pte; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci writel_relaxed(iova, gart->regs + GART_ENTRY_ADDR); 788c2ecf20Sopenharmony_ci pte = readl_relaxed(gart->regs + GART_ENTRY_DATA); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci return pte; 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic void do_gart_setup(struct gart_device *gart, const u32 *data) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci unsigned long iova; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci for_each_gart_pte(gart, iova) 888c2ecf20Sopenharmony_ci gart_set_pte(gart, iova, data ? *(data++) : 0); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci writel_relaxed(1, gart->regs + GART_CONFIG); 918c2ecf20Sopenharmony_ci FLUSH_GART_REGS(gart); 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic inline bool gart_iova_range_invalid(struct gart_device *gart, 958c2ecf20Sopenharmony_ci unsigned long iova, size_t bytes) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci return unlikely(iova < gart->iovmm_base || bytes != GART_PAGE_SIZE || 988c2ecf20Sopenharmony_ci iova + bytes > gart->iovmm_end); 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic inline bool gart_pte_valid(struct gart_device *gart, unsigned long iova) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci return !!(gart_read_pte(gart, iova) & GART_ENTRY_PHYS_ADDR_VALID); 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic int gart_iommu_attach_dev(struct iommu_domain *domain, 1078c2ecf20Sopenharmony_ci struct device *dev) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci struct gart_device *gart = gart_handle; 1108c2ecf20Sopenharmony_ci int ret = 0; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci spin_lock(&gart->dom_lock); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci if (gart->active_domain && gart->active_domain != domain) { 1158c2ecf20Sopenharmony_ci ret = -EBUSY; 1168c2ecf20Sopenharmony_ci } else if (dev_iommu_priv_get(dev) != domain) { 1178c2ecf20Sopenharmony_ci dev_iommu_priv_set(dev, domain); 1188c2ecf20Sopenharmony_ci gart->active_domain = domain; 1198c2ecf20Sopenharmony_ci gart->active_devices++; 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci spin_unlock(&gart->dom_lock); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci return ret; 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic void gart_iommu_detach_dev(struct iommu_domain *domain, 1288c2ecf20Sopenharmony_ci struct device *dev) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci struct gart_device *gart = gart_handle; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci spin_lock(&gart->dom_lock); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci if (dev_iommu_priv_get(dev) == domain) { 1358c2ecf20Sopenharmony_ci dev_iommu_priv_set(dev, NULL); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci if (--gart->active_devices == 0) 1388c2ecf20Sopenharmony_ci gart->active_domain = NULL; 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci spin_unlock(&gart->dom_lock); 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic struct iommu_domain *gart_iommu_domain_alloc(unsigned type) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci struct iommu_domain *domain; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci if (type != IOMMU_DOMAIN_UNMANAGED) 1498c2ecf20Sopenharmony_ci return NULL; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci domain = kzalloc(sizeof(*domain), GFP_KERNEL); 1528c2ecf20Sopenharmony_ci if (domain) { 1538c2ecf20Sopenharmony_ci domain->geometry.aperture_start = gart_handle->iovmm_base; 1548c2ecf20Sopenharmony_ci domain->geometry.aperture_end = gart_handle->iovmm_end - 1; 1558c2ecf20Sopenharmony_ci domain->geometry.force_aperture = true; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci return domain; 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic void gart_iommu_domain_free(struct iommu_domain *domain) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci WARN_ON(gart_handle->active_domain == domain); 1648c2ecf20Sopenharmony_ci kfree(domain); 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic inline int __gart_iommu_map(struct gart_device *gart, unsigned long iova, 1688c2ecf20Sopenharmony_ci unsigned long pa) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci if (unlikely(gart_debug && gart_pte_valid(gart, iova))) { 1718c2ecf20Sopenharmony_ci dev_err(gart->dev, "Page entry is in-use\n"); 1728c2ecf20Sopenharmony_ci return -EINVAL; 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci gart_set_pte(gart, iova, GART_ENTRY_PHYS_ADDR_VALID | pa); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci return 0; 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic int gart_iommu_map(struct iommu_domain *domain, unsigned long iova, 1818c2ecf20Sopenharmony_ci phys_addr_t pa, size_t bytes, int prot, gfp_t gfp) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci struct gart_device *gart = gart_handle; 1848c2ecf20Sopenharmony_ci int ret; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci if (gart_iova_range_invalid(gart, iova, bytes)) 1878c2ecf20Sopenharmony_ci return -EINVAL; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci spin_lock(&gart->pte_lock); 1908c2ecf20Sopenharmony_ci ret = __gart_iommu_map(gart, iova, (unsigned long)pa); 1918c2ecf20Sopenharmony_ci spin_unlock(&gart->pte_lock); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci return ret; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic inline int __gart_iommu_unmap(struct gart_device *gart, 1978c2ecf20Sopenharmony_ci unsigned long iova) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci if (unlikely(gart_debug && !gart_pte_valid(gart, iova))) { 2008c2ecf20Sopenharmony_ci dev_err(gart->dev, "Page entry is invalid\n"); 2018c2ecf20Sopenharmony_ci return -EINVAL; 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci gart_set_pte(gart, iova, 0); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci return 0; 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic size_t gart_iommu_unmap(struct iommu_domain *domain, unsigned long iova, 2108c2ecf20Sopenharmony_ci size_t bytes, struct iommu_iotlb_gather *gather) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci struct gart_device *gart = gart_handle; 2138c2ecf20Sopenharmony_ci int err; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci if (gart_iova_range_invalid(gart, iova, bytes)) 2168c2ecf20Sopenharmony_ci return 0; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci spin_lock(&gart->pte_lock); 2198c2ecf20Sopenharmony_ci err = __gart_iommu_unmap(gart, iova); 2208c2ecf20Sopenharmony_ci spin_unlock(&gart->pte_lock); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci return err ? 0 : bytes; 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic phys_addr_t gart_iommu_iova_to_phys(struct iommu_domain *domain, 2268c2ecf20Sopenharmony_ci dma_addr_t iova) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci struct gart_device *gart = gart_handle; 2298c2ecf20Sopenharmony_ci unsigned long pte; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci if (gart_iova_range_invalid(gart, iova, GART_PAGE_SIZE)) 2328c2ecf20Sopenharmony_ci return -EINVAL; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci spin_lock(&gart->pte_lock); 2358c2ecf20Sopenharmony_ci pte = gart_read_pte(gart, iova); 2368c2ecf20Sopenharmony_ci spin_unlock(&gart->pte_lock); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci return pte & GART_PAGE_MASK; 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic bool gart_iommu_capable(enum iommu_cap cap) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci return false; 2448c2ecf20Sopenharmony_ci} 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_cistatic struct iommu_device *gart_iommu_probe_device(struct device *dev) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci if (!dev_iommu_fwspec_get(dev)) 2498c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci return &gart_handle->iommu; 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistatic void gart_iommu_release_device(struct device *dev) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_cistatic int gart_iommu_of_xlate(struct device *dev, 2598c2ecf20Sopenharmony_ci struct of_phandle_args *args) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci return 0; 2628c2ecf20Sopenharmony_ci} 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_cistatic void gart_iommu_sync_map(struct iommu_domain *domain) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci FLUSH_GART_REGS(gart_handle); 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic void gart_iommu_sync(struct iommu_domain *domain, 2708c2ecf20Sopenharmony_ci struct iommu_iotlb_gather *gather) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci gart_iommu_sync_map(domain); 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic const struct iommu_ops gart_iommu_ops = { 2768c2ecf20Sopenharmony_ci .capable = gart_iommu_capable, 2778c2ecf20Sopenharmony_ci .domain_alloc = gart_iommu_domain_alloc, 2788c2ecf20Sopenharmony_ci .domain_free = gart_iommu_domain_free, 2798c2ecf20Sopenharmony_ci .attach_dev = gart_iommu_attach_dev, 2808c2ecf20Sopenharmony_ci .detach_dev = gart_iommu_detach_dev, 2818c2ecf20Sopenharmony_ci .probe_device = gart_iommu_probe_device, 2828c2ecf20Sopenharmony_ci .release_device = gart_iommu_release_device, 2838c2ecf20Sopenharmony_ci .device_group = generic_device_group, 2848c2ecf20Sopenharmony_ci .map = gart_iommu_map, 2858c2ecf20Sopenharmony_ci .unmap = gart_iommu_unmap, 2868c2ecf20Sopenharmony_ci .iova_to_phys = gart_iommu_iova_to_phys, 2878c2ecf20Sopenharmony_ci .pgsize_bitmap = GART_IOMMU_PGSIZES, 2888c2ecf20Sopenharmony_ci .of_xlate = gart_iommu_of_xlate, 2898c2ecf20Sopenharmony_ci .iotlb_sync_map = gart_iommu_sync_map, 2908c2ecf20Sopenharmony_ci .iotlb_sync = gart_iommu_sync, 2918c2ecf20Sopenharmony_ci}; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ciint tegra_gart_suspend(struct gart_device *gart) 2948c2ecf20Sopenharmony_ci{ 2958c2ecf20Sopenharmony_ci u32 *data = gart->savedata; 2968c2ecf20Sopenharmony_ci unsigned long iova; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci /* 2998c2ecf20Sopenharmony_ci * All GART users shall be suspended at this point. Disable 3008c2ecf20Sopenharmony_ci * address translation to trap all GART accesses as invalid 3018c2ecf20Sopenharmony_ci * memory accesses. 3028c2ecf20Sopenharmony_ci */ 3038c2ecf20Sopenharmony_ci writel_relaxed(0, gart->regs + GART_CONFIG); 3048c2ecf20Sopenharmony_ci FLUSH_GART_REGS(gart); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci for_each_gart_pte(gart, iova) 3078c2ecf20Sopenharmony_ci *(data++) = gart_read_pte(gart, iova); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci return 0; 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ciint tegra_gart_resume(struct gart_device *gart) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci do_gart_setup(gart, gart->savedata); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci return 0; 3178c2ecf20Sopenharmony_ci} 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_cistruct gart_device *tegra_gart_probe(struct device *dev, struct tegra_mc *mc) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci struct gart_device *gart; 3228c2ecf20Sopenharmony_ci struct resource *res; 3238c2ecf20Sopenharmony_ci int err; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci BUILD_BUG_ON(PAGE_SHIFT != GART_PAGE_SHIFT); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci /* the GART memory aperture is required */ 3288c2ecf20Sopenharmony_ci res = platform_get_resource(to_platform_device(dev), IORESOURCE_MEM, 1); 3298c2ecf20Sopenharmony_ci if (!res) { 3308c2ecf20Sopenharmony_ci dev_err(dev, "Memory aperture resource unavailable\n"); 3318c2ecf20Sopenharmony_ci return ERR_PTR(-ENXIO); 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci gart = kzalloc(sizeof(*gart), GFP_KERNEL); 3358c2ecf20Sopenharmony_ci if (!gart) 3368c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci gart_handle = gart; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci gart->dev = dev; 3418c2ecf20Sopenharmony_ci gart->regs = mc->regs + GART_REG_BASE; 3428c2ecf20Sopenharmony_ci gart->iovmm_base = res->start; 3438c2ecf20Sopenharmony_ci gart->iovmm_end = res->end + 1; 3448c2ecf20Sopenharmony_ci spin_lock_init(&gart->pte_lock); 3458c2ecf20Sopenharmony_ci spin_lock_init(&gart->dom_lock); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci do_gart_setup(gart, NULL); 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci err = iommu_device_sysfs_add(&gart->iommu, dev, NULL, "gart"); 3508c2ecf20Sopenharmony_ci if (err) 3518c2ecf20Sopenharmony_ci goto free_gart; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci iommu_device_set_ops(&gart->iommu, &gart_iommu_ops); 3548c2ecf20Sopenharmony_ci iommu_device_set_fwnode(&gart->iommu, dev->fwnode); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci err = iommu_device_register(&gart->iommu); 3578c2ecf20Sopenharmony_ci if (err) 3588c2ecf20Sopenharmony_ci goto remove_sysfs; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci gart->savedata = vmalloc(resource_size(res) / GART_PAGE_SIZE * 3618c2ecf20Sopenharmony_ci sizeof(u32)); 3628c2ecf20Sopenharmony_ci if (!gart->savedata) { 3638c2ecf20Sopenharmony_ci err = -ENOMEM; 3648c2ecf20Sopenharmony_ci goto unregister_iommu; 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci return gart; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ciunregister_iommu: 3708c2ecf20Sopenharmony_ci iommu_device_unregister(&gart->iommu); 3718c2ecf20Sopenharmony_ciremove_sysfs: 3728c2ecf20Sopenharmony_ci iommu_device_sysfs_remove(&gart->iommu); 3738c2ecf20Sopenharmony_cifree_gart: 3748c2ecf20Sopenharmony_ci kfree(gart); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci return ERR_PTR(err); 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_cimodule_param(gart_debug, bool, 0644); 3808c2ecf20Sopenharmony_ciMODULE_PARM_DESC(gart_debug, "Enable GART debugging"); 381