18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Tegra host1x driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2010-2013, NVIDIA Corporation. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/clk.h> 98c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 108c2ecf20Sopenharmony_ci#include <linux/io.h> 118c2ecf20Sopenharmony_ci#include <linux/list.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/of_device.h> 148c2ecf20Sopenharmony_ci#include <linux/of.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#define CREATE_TRACE_POINTS 188c2ecf20Sopenharmony_ci#include <trace/events/host1x.h> 198c2ecf20Sopenharmony_ci#undef CREATE_TRACE_POINTS 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU) 228c2ecf20Sopenharmony_ci#include <asm/dma-iommu.h> 238c2ecf20Sopenharmony_ci#endif 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include "bus.h" 268c2ecf20Sopenharmony_ci#include "channel.h" 278c2ecf20Sopenharmony_ci#include "debug.h" 288c2ecf20Sopenharmony_ci#include "dev.h" 298c2ecf20Sopenharmony_ci#include "intr.h" 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#include "hw/host1x01.h" 328c2ecf20Sopenharmony_ci#include "hw/host1x02.h" 338c2ecf20Sopenharmony_ci#include "hw/host1x04.h" 348c2ecf20Sopenharmony_ci#include "hw/host1x05.h" 358c2ecf20Sopenharmony_ci#include "hw/host1x06.h" 368c2ecf20Sopenharmony_ci#include "hw/host1x07.h" 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_civoid host1x_hypervisor_writel(struct host1x *host1x, u32 v, u32 r) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci writel(v, host1x->hv_regs + r); 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ciu32 host1x_hypervisor_readl(struct host1x *host1x, u32 r) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci return readl(host1x->hv_regs + r); 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_civoid host1x_sync_writel(struct host1x *host1x, u32 v, u32 r) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci void __iomem *sync_regs = host1x->regs + host1x->info->sync_offset; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci writel(v, sync_regs + r); 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ciu32 host1x_sync_readl(struct host1x *host1x, u32 r) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci void __iomem *sync_regs = host1x->regs + host1x->info->sync_offset; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci return readl(sync_regs + r); 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_civoid host1x_ch_writel(struct host1x_channel *ch, u32 v, u32 r) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci writel(v, ch->regs + r); 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ciu32 host1x_ch_readl(struct host1x_channel *ch, u32 r) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci return readl(ch->regs + r); 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic const struct host1x_info host1x01_info = { 738c2ecf20Sopenharmony_ci .nb_channels = 8, 748c2ecf20Sopenharmony_ci .nb_pts = 32, 758c2ecf20Sopenharmony_ci .nb_mlocks = 16, 768c2ecf20Sopenharmony_ci .nb_bases = 8, 778c2ecf20Sopenharmony_ci .init = host1x01_init, 788c2ecf20Sopenharmony_ci .sync_offset = 0x3000, 798c2ecf20Sopenharmony_ci .dma_mask = DMA_BIT_MASK(32), 808c2ecf20Sopenharmony_ci .has_wide_gather = false, 818c2ecf20Sopenharmony_ci .has_hypervisor = false, 828c2ecf20Sopenharmony_ci .num_sid_entries = 0, 838c2ecf20Sopenharmony_ci .sid_table = NULL, 848c2ecf20Sopenharmony_ci}; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic const struct host1x_info host1x02_info = { 878c2ecf20Sopenharmony_ci .nb_channels = 9, 888c2ecf20Sopenharmony_ci .nb_pts = 32, 898c2ecf20Sopenharmony_ci .nb_mlocks = 16, 908c2ecf20Sopenharmony_ci .nb_bases = 12, 918c2ecf20Sopenharmony_ci .init = host1x02_init, 928c2ecf20Sopenharmony_ci .sync_offset = 0x3000, 938c2ecf20Sopenharmony_ci .dma_mask = DMA_BIT_MASK(32), 948c2ecf20Sopenharmony_ci .has_wide_gather = false, 958c2ecf20Sopenharmony_ci .has_hypervisor = false, 968c2ecf20Sopenharmony_ci .num_sid_entries = 0, 978c2ecf20Sopenharmony_ci .sid_table = NULL, 988c2ecf20Sopenharmony_ci}; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic const struct host1x_info host1x04_info = { 1018c2ecf20Sopenharmony_ci .nb_channels = 12, 1028c2ecf20Sopenharmony_ci .nb_pts = 192, 1038c2ecf20Sopenharmony_ci .nb_mlocks = 16, 1048c2ecf20Sopenharmony_ci .nb_bases = 64, 1058c2ecf20Sopenharmony_ci .init = host1x04_init, 1068c2ecf20Sopenharmony_ci .sync_offset = 0x2100, 1078c2ecf20Sopenharmony_ci .dma_mask = DMA_BIT_MASK(34), 1088c2ecf20Sopenharmony_ci .has_wide_gather = false, 1098c2ecf20Sopenharmony_ci .has_hypervisor = false, 1108c2ecf20Sopenharmony_ci .num_sid_entries = 0, 1118c2ecf20Sopenharmony_ci .sid_table = NULL, 1128c2ecf20Sopenharmony_ci}; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic const struct host1x_info host1x05_info = { 1158c2ecf20Sopenharmony_ci .nb_channels = 14, 1168c2ecf20Sopenharmony_ci .nb_pts = 192, 1178c2ecf20Sopenharmony_ci .nb_mlocks = 16, 1188c2ecf20Sopenharmony_ci .nb_bases = 64, 1198c2ecf20Sopenharmony_ci .init = host1x05_init, 1208c2ecf20Sopenharmony_ci .sync_offset = 0x2100, 1218c2ecf20Sopenharmony_ci .dma_mask = DMA_BIT_MASK(34), 1228c2ecf20Sopenharmony_ci .has_wide_gather = false, 1238c2ecf20Sopenharmony_ci .has_hypervisor = false, 1248c2ecf20Sopenharmony_ci .num_sid_entries = 0, 1258c2ecf20Sopenharmony_ci .sid_table = NULL, 1268c2ecf20Sopenharmony_ci}; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic const struct host1x_sid_entry tegra186_sid_table[] = { 1298c2ecf20Sopenharmony_ci { 1308c2ecf20Sopenharmony_ci /* VIC */ 1318c2ecf20Sopenharmony_ci .base = 0x1af0, 1328c2ecf20Sopenharmony_ci .offset = 0x30, 1338c2ecf20Sopenharmony_ci .limit = 0x34 1348c2ecf20Sopenharmony_ci }, 1358c2ecf20Sopenharmony_ci}; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic const struct host1x_info host1x06_info = { 1388c2ecf20Sopenharmony_ci .nb_channels = 63, 1398c2ecf20Sopenharmony_ci .nb_pts = 576, 1408c2ecf20Sopenharmony_ci .nb_mlocks = 24, 1418c2ecf20Sopenharmony_ci .nb_bases = 16, 1428c2ecf20Sopenharmony_ci .init = host1x06_init, 1438c2ecf20Sopenharmony_ci .sync_offset = 0x0, 1448c2ecf20Sopenharmony_ci .dma_mask = DMA_BIT_MASK(40), 1458c2ecf20Sopenharmony_ci .has_wide_gather = true, 1468c2ecf20Sopenharmony_ci .has_hypervisor = true, 1478c2ecf20Sopenharmony_ci .num_sid_entries = ARRAY_SIZE(tegra186_sid_table), 1488c2ecf20Sopenharmony_ci .sid_table = tegra186_sid_table, 1498c2ecf20Sopenharmony_ci}; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic const struct host1x_sid_entry tegra194_sid_table[] = { 1528c2ecf20Sopenharmony_ci { 1538c2ecf20Sopenharmony_ci /* VIC */ 1548c2ecf20Sopenharmony_ci .base = 0x1af0, 1558c2ecf20Sopenharmony_ci .offset = 0x30, 1568c2ecf20Sopenharmony_ci .limit = 0x34 1578c2ecf20Sopenharmony_ci }, 1588c2ecf20Sopenharmony_ci}; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic const struct host1x_info host1x07_info = { 1618c2ecf20Sopenharmony_ci .nb_channels = 63, 1628c2ecf20Sopenharmony_ci .nb_pts = 704, 1638c2ecf20Sopenharmony_ci .nb_mlocks = 32, 1648c2ecf20Sopenharmony_ci .nb_bases = 0, 1658c2ecf20Sopenharmony_ci .init = host1x07_init, 1668c2ecf20Sopenharmony_ci .sync_offset = 0x0, 1678c2ecf20Sopenharmony_ci .dma_mask = DMA_BIT_MASK(40), 1688c2ecf20Sopenharmony_ci .has_wide_gather = true, 1698c2ecf20Sopenharmony_ci .has_hypervisor = true, 1708c2ecf20Sopenharmony_ci .num_sid_entries = ARRAY_SIZE(tegra194_sid_table), 1718c2ecf20Sopenharmony_ci .sid_table = tegra194_sid_table, 1728c2ecf20Sopenharmony_ci}; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic const struct of_device_id host1x_of_match[] = { 1758c2ecf20Sopenharmony_ci { .compatible = "nvidia,tegra194-host1x", .data = &host1x07_info, }, 1768c2ecf20Sopenharmony_ci { .compatible = "nvidia,tegra186-host1x", .data = &host1x06_info, }, 1778c2ecf20Sopenharmony_ci { .compatible = "nvidia,tegra210-host1x", .data = &host1x05_info, }, 1788c2ecf20Sopenharmony_ci { .compatible = "nvidia,tegra124-host1x", .data = &host1x04_info, }, 1798c2ecf20Sopenharmony_ci { .compatible = "nvidia,tegra114-host1x", .data = &host1x02_info, }, 1808c2ecf20Sopenharmony_ci { .compatible = "nvidia,tegra30-host1x", .data = &host1x01_info, }, 1818c2ecf20Sopenharmony_ci { .compatible = "nvidia,tegra20-host1x", .data = &host1x01_info, }, 1828c2ecf20Sopenharmony_ci { }, 1838c2ecf20Sopenharmony_ci}; 1848c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, host1x_of_match); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic void host1x_setup_sid_table(struct host1x *host) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci const struct host1x_info *info = host->info; 1898c2ecf20Sopenharmony_ci unsigned int i; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci for (i = 0; i < info->num_sid_entries; i++) { 1928c2ecf20Sopenharmony_ci const struct host1x_sid_entry *entry = &info->sid_table[i]; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci host1x_hypervisor_writel(host, entry->offset, entry->base); 1958c2ecf20Sopenharmony_ci host1x_hypervisor_writel(host, entry->limit, entry->base + 4); 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cistatic bool host1x_wants_iommu(struct host1x *host1x) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci /* Our IOMMU usage policy doesn't currently play well with GART */ 2028c2ecf20Sopenharmony_ci if (of_machine_is_compatible("nvidia,tegra20")) 2038c2ecf20Sopenharmony_ci return false; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci /* 2068c2ecf20Sopenharmony_ci * If we support addressing a maximum of 32 bits of physical memory 2078c2ecf20Sopenharmony_ci * and if the host1x firewall is enabled, there's no need to enable 2088c2ecf20Sopenharmony_ci * IOMMU support. This can happen for example on Tegra20, Tegra30 2098c2ecf20Sopenharmony_ci * and Tegra114. 2108c2ecf20Sopenharmony_ci * 2118c2ecf20Sopenharmony_ci * Tegra124 and later can address up to 34 bits of physical memory and 2128c2ecf20Sopenharmony_ci * many platforms come equipped with more than 2 GiB of system memory, 2138c2ecf20Sopenharmony_ci * which requires crossing the 4 GiB boundary. But there's a catch: on 2148c2ecf20Sopenharmony_ci * SoCs before Tegra186 (i.e. Tegra124 and Tegra210), the host1x can 2158c2ecf20Sopenharmony_ci * only address up to 32 bits of memory in GATHER opcodes, which means 2168c2ecf20Sopenharmony_ci * that command buffers need to either be in the first 2 GiB of system 2178c2ecf20Sopenharmony_ci * memory (which could quickly lead to memory exhaustion), or command 2188c2ecf20Sopenharmony_ci * buffers need to be treated differently from other buffers (which is 2198c2ecf20Sopenharmony_ci * not possible with the current ABI). 2208c2ecf20Sopenharmony_ci * 2218c2ecf20Sopenharmony_ci * A third option is to use the IOMMU in these cases to make sure all 2228c2ecf20Sopenharmony_ci * buffers will be mapped into a 32-bit IOVA space that host1x can 2238c2ecf20Sopenharmony_ci * address. This allows all of the system memory to be used and works 2248c2ecf20Sopenharmony_ci * within the limitations of the host1x on these SoCs. 2258c2ecf20Sopenharmony_ci * 2268c2ecf20Sopenharmony_ci * In summary, default to enable IOMMU on Tegra124 and later. For any 2278c2ecf20Sopenharmony_ci * of the earlier SoCs, only use the IOMMU for additional safety when 2288c2ecf20Sopenharmony_ci * the host1x firewall is disabled. 2298c2ecf20Sopenharmony_ci */ 2308c2ecf20Sopenharmony_ci if (host1x->info->dma_mask <= DMA_BIT_MASK(32)) { 2318c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_TEGRA_HOST1X_FIREWALL)) 2328c2ecf20Sopenharmony_ci return false; 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci return true; 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic struct iommu_domain *host1x_iommu_attach(struct host1x *host) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci struct iommu_domain *domain = iommu_get_domain_for_dev(host->dev); 2418c2ecf20Sopenharmony_ci int err; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU) 2448c2ecf20Sopenharmony_ci if (host->dev->archdata.mapping) { 2458c2ecf20Sopenharmony_ci struct dma_iommu_mapping *mapping = 2468c2ecf20Sopenharmony_ci to_dma_iommu_mapping(host->dev); 2478c2ecf20Sopenharmony_ci arm_iommu_detach_device(host->dev); 2488c2ecf20Sopenharmony_ci arm_iommu_release_mapping(mapping); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci domain = iommu_get_domain_for_dev(host->dev); 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci#endif 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci /* 2558c2ecf20Sopenharmony_ci * We may not always want to enable IOMMU support (for example if the 2568c2ecf20Sopenharmony_ci * host1x firewall is already enabled and we don't support addressing 2578c2ecf20Sopenharmony_ci * more than 32 bits of physical memory), so check for that first. 2588c2ecf20Sopenharmony_ci * 2598c2ecf20Sopenharmony_ci * Similarly, if host1x is already attached to an IOMMU (via the DMA 2608c2ecf20Sopenharmony_ci * API), don't try to attach again. 2618c2ecf20Sopenharmony_ci */ 2628c2ecf20Sopenharmony_ci if (!host1x_wants_iommu(host) || domain) 2638c2ecf20Sopenharmony_ci return domain; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci host->group = iommu_group_get(host->dev); 2668c2ecf20Sopenharmony_ci if (host->group) { 2678c2ecf20Sopenharmony_ci struct iommu_domain_geometry *geometry; 2688c2ecf20Sopenharmony_ci dma_addr_t start, end; 2698c2ecf20Sopenharmony_ci unsigned long order; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci err = iova_cache_get(); 2728c2ecf20Sopenharmony_ci if (err < 0) 2738c2ecf20Sopenharmony_ci goto put_group; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci host->domain = iommu_domain_alloc(&platform_bus_type); 2768c2ecf20Sopenharmony_ci if (!host->domain) { 2778c2ecf20Sopenharmony_ci err = -ENOMEM; 2788c2ecf20Sopenharmony_ci goto put_cache; 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci err = iommu_attach_group(host->domain, host->group); 2828c2ecf20Sopenharmony_ci if (err) { 2838c2ecf20Sopenharmony_ci if (err == -ENODEV) 2848c2ecf20Sopenharmony_ci err = 0; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci goto free_domain; 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci geometry = &host->domain->geometry; 2908c2ecf20Sopenharmony_ci start = geometry->aperture_start & host->info->dma_mask; 2918c2ecf20Sopenharmony_ci end = geometry->aperture_end & host->info->dma_mask; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci order = __ffs(host->domain->pgsize_bitmap); 2948c2ecf20Sopenharmony_ci init_iova_domain(&host->iova, 1UL << order, start >> order); 2958c2ecf20Sopenharmony_ci host->iova_end = end; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci domain = host->domain; 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci return domain; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cifree_domain: 3038c2ecf20Sopenharmony_ci iommu_domain_free(host->domain); 3048c2ecf20Sopenharmony_ci host->domain = NULL; 3058c2ecf20Sopenharmony_ciput_cache: 3068c2ecf20Sopenharmony_ci iova_cache_put(); 3078c2ecf20Sopenharmony_ciput_group: 3088c2ecf20Sopenharmony_ci iommu_group_put(host->group); 3098c2ecf20Sopenharmony_ci host->group = NULL; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci return ERR_PTR(err); 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_cistatic int host1x_iommu_init(struct host1x *host) 3158c2ecf20Sopenharmony_ci{ 3168c2ecf20Sopenharmony_ci u64 mask = host->info->dma_mask; 3178c2ecf20Sopenharmony_ci struct iommu_domain *domain; 3188c2ecf20Sopenharmony_ci int err; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci domain = host1x_iommu_attach(host); 3218c2ecf20Sopenharmony_ci if (IS_ERR(domain)) { 3228c2ecf20Sopenharmony_ci err = PTR_ERR(domain); 3238c2ecf20Sopenharmony_ci dev_err(host->dev, "failed to attach to IOMMU: %d\n", err); 3248c2ecf20Sopenharmony_ci return err; 3258c2ecf20Sopenharmony_ci } 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci /* 3288c2ecf20Sopenharmony_ci * If we're not behind an IOMMU make sure we don't get push buffers 3298c2ecf20Sopenharmony_ci * that are allocated outside of the range addressable by the GATHER 3308c2ecf20Sopenharmony_ci * opcode. 3318c2ecf20Sopenharmony_ci * 3328c2ecf20Sopenharmony_ci * Newer generations of Tegra (Tegra186 and later) support a wide 3338c2ecf20Sopenharmony_ci * variant of the GATHER opcode that allows addressing more bits. 3348c2ecf20Sopenharmony_ci */ 3358c2ecf20Sopenharmony_ci if (!domain && !host->info->has_wide_gather) 3368c2ecf20Sopenharmony_ci mask = DMA_BIT_MASK(32); 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci err = dma_coerce_mask_and_coherent(host->dev, mask); 3398c2ecf20Sopenharmony_ci if (err < 0) { 3408c2ecf20Sopenharmony_ci dev_err(host->dev, "failed to set DMA mask: %d\n", err); 3418c2ecf20Sopenharmony_ci return err; 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci return 0; 3458c2ecf20Sopenharmony_ci} 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cistatic void host1x_iommu_exit(struct host1x *host) 3488c2ecf20Sopenharmony_ci{ 3498c2ecf20Sopenharmony_ci if (host->domain) { 3508c2ecf20Sopenharmony_ci put_iova_domain(&host->iova); 3518c2ecf20Sopenharmony_ci iommu_detach_group(host->domain, host->group); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci iommu_domain_free(host->domain); 3548c2ecf20Sopenharmony_ci host->domain = NULL; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci iova_cache_put(); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci iommu_group_put(host->group); 3598c2ecf20Sopenharmony_ci host->group = NULL; 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci} 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_cistatic int host1x_probe(struct platform_device *pdev) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci struct host1x *host; 3668c2ecf20Sopenharmony_ci struct resource *regs, *hv_regs = NULL; 3678c2ecf20Sopenharmony_ci int syncpt_irq; 3688c2ecf20Sopenharmony_ci int err; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL); 3718c2ecf20Sopenharmony_ci if (!host) 3728c2ecf20Sopenharmony_ci return -ENOMEM; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci host->info = of_device_get_match_data(&pdev->dev); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci if (host->info->has_hypervisor) { 3778c2ecf20Sopenharmony_ci regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vm"); 3788c2ecf20Sopenharmony_ci if (!regs) { 3798c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to get vm registers\n"); 3808c2ecf20Sopenharmony_ci return -ENXIO; 3818c2ecf20Sopenharmony_ci } 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci hv_regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, 3848c2ecf20Sopenharmony_ci "hypervisor"); 3858c2ecf20Sopenharmony_ci if (!hv_regs) { 3868c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 3878c2ecf20Sopenharmony_ci "failed to get hypervisor registers\n"); 3888c2ecf20Sopenharmony_ci return -ENXIO; 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci } else { 3918c2ecf20Sopenharmony_ci regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); 3928c2ecf20Sopenharmony_ci if (!regs) { 3938c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to get registers\n"); 3948c2ecf20Sopenharmony_ci return -ENXIO; 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci syncpt_irq = platform_get_irq(pdev, 0); 3998c2ecf20Sopenharmony_ci if (syncpt_irq < 0) 4008c2ecf20Sopenharmony_ci return syncpt_irq; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci mutex_init(&host->devices_lock); 4038c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&host->devices); 4048c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&host->list); 4058c2ecf20Sopenharmony_ci host->dev = &pdev->dev; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci /* set common host1x device data */ 4088c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, host); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci host->regs = devm_ioremap_resource(&pdev->dev, regs); 4118c2ecf20Sopenharmony_ci if (IS_ERR(host->regs)) 4128c2ecf20Sopenharmony_ci return PTR_ERR(host->regs); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci if (host->info->has_hypervisor) { 4158c2ecf20Sopenharmony_ci host->hv_regs = devm_ioremap_resource(&pdev->dev, hv_regs); 4168c2ecf20Sopenharmony_ci if (IS_ERR(host->hv_regs)) 4178c2ecf20Sopenharmony_ci return PTR_ERR(host->hv_regs); 4188c2ecf20Sopenharmony_ci } 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci host->dev->dma_parms = &host->dma_parms; 4218c2ecf20Sopenharmony_ci dma_set_max_seg_size(host->dev, UINT_MAX); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci if (host->info->init) { 4248c2ecf20Sopenharmony_ci err = host->info->init(host); 4258c2ecf20Sopenharmony_ci if (err) 4268c2ecf20Sopenharmony_ci return err; 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci host->clk = devm_clk_get(&pdev->dev, NULL); 4308c2ecf20Sopenharmony_ci if (IS_ERR(host->clk)) { 4318c2ecf20Sopenharmony_ci err = PTR_ERR(host->clk); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci if (err != -EPROBE_DEFER) 4348c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to get clock: %d\n", err); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci return err; 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci host->rst = devm_reset_control_get(&pdev->dev, "host1x"); 4408c2ecf20Sopenharmony_ci if (IS_ERR(host->rst)) { 4418c2ecf20Sopenharmony_ci err = PTR_ERR(host->rst); 4428c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to get reset: %d\n", err); 4438c2ecf20Sopenharmony_ci return err; 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci err = host1x_iommu_init(host); 4478c2ecf20Sopenharmony_ci if (err < 0) { 4488c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to setup IOMMU: %d\n", err); 4498c2ecf20Sopenharmony_ci return err; 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci err = host1x_channel_list_init(&host->channel_list, 4538c2ecf20Sopenharmony_ci host->info->nb_channels); 4548c2ecf20Sopenharmony_ci if (err) { 4558c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to initialize channel list\n"); 4568c2ecf20Sopenharmony_ci goto iommu_exit; 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci err = clk_prepare_enable(host->clk); 4608c2ecf20Sopenharmony_ci if (err < 0) { 4618c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to enable clock\n"); 4628c2ecf20Sopenharmony_ci goto free_channels; 4638c2ecf20Sopenharmony_ci } 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci err = reset_control_deassert(host->rst); 4668c2ecf20Sopenharmony_ci if (err < 0) { 4678c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to deassert reset: %d\n", err); 4688c2ecf20Sopenharmony_ci goto unprepare_disable; 4698c2ecf20Sopenharmony_ci } 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci err = host1x_syncpt_init(host); 4728c2ecf20Sopenharmony_ci if (err) { 4738c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to initialize syncpts\n"); 4748c2ecf20Sopenharmony_ci goto reset_assert; 4758c2ecf20Sopenharmony_ci } 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci err = host1x_intr_init(host, syncpt_irq); 4788c2ecf20Sopenharmony_ci if (err) { 4798c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to initialize interrupts\n"); 4808c2ecf20Sopenharmony_ci goto deinit_syncpt; 4818c2ecf20Sopenharmony_ci } 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci host1x_debug_init(host); 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci if (host->info->has_hypervisor) 4868c2ecf20Sopenharmony_ci host1x_setup_sid_table(host); 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci err = host1x_register(host); 4898c2ecf20Sopenharmony_ci if (err < 0) 4908c2ecf20Sopenharmony_ci goto deinit_debugfs; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci err = devm_of_platform_populate(&pdev->dev); 4938c2ecf20Sopenharmony_ci if (err < 0) 4948c2ecf20Sopenharmony_ci goto unregister; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci return 0; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ciunregister: 4998c2ecf20Sopenharmony_ci host1x_unregister(host); 5008c2ecf20Sopenharmony_cideinit_debugfs: 5018c2ecf20Sopenharmony_ci host1x_debug_deinit(host); 5028c2ecf20Sopenharmony_ci host1x_intr_deinit(host); 5038c2ecf20Sopenharmony_cideinit_syncpt: 5048c2ecf20Sopenharmony_ci host1x_syncpt_deinit(host); 5058c2ecf20Sopenharmony_cireset_assert: 5068c2ecf20Sopenharmony_ci reset_control_assert(host->rst); 5078c2ecf20Sopenharmony_ciunprepare_disable: 5088c2ecf20Sopenharmony_ci clk_disable_unprepare(host->clk); 5098c2ecf20Sopenharmony_cifree_channels: 5108c2ecf20Sopenharmony_ci host1x_channel_list_free(&host->channel_list); 5118c2ecf20Sopenharmony_ciiommu_exit: 5128c2ecf20Sopenharmony_ci host1x_iommu_exit(host); 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci return err; 5158c2ecf20Sopenharmony_ci} 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_cistatic int host1x_remove(struct platform_device *pdev) 5188c2ecf20Sopenharmony_ci{ 5198c2ecf20Sopenharmony_ci struct host1x *host = platform_get_drvdata(pdev); 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci host1x_unregister(host); 5228c2ecf20Sopenharmony_ci host1x_debug_deinit(host); 5238c2ecf20Sopenharmony_ci host1x_intr_deinit(host); 5248c2ecf20Sopenharmony_ci host1x_syncpt_deinit(host); 5258c2ecf20Sopenharmony_ci reset_control_assert(host->rst); 5268c2ecf20Sopenharmony_ci clk_disable_unprepare(host->clk); 5278c2ecf20Sopenharmony_ci host1x_channel_list_free(&host->channel_list); 5288c2ecf20Sopenharmony_ci host1x_iommu_exit(host); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci return 0; 5318c2ecf20Sopenharmony_ci} 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_cistatic struct platform_driver tegra_host1x_driver = { 5348c2ecf20Sopenharmony_ci .driver = { 5358c2ecf20Sopenharmony_ci .name = "tegra-host1x", 5368c2ecf20Sopenharmony_ci .of_match_table = host1x_of_match, 5378c2ecf20Sopenharmony_ci }, 5388c2ecf20Sopenharmony_ci .probe = host1x_probe, 5398c2ecf20Sopenharmony_ci .remove = host1x_remove, 5408c2ecf20Sopenharmony_ci}; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_cistatic struct platform_driver * const drivers[] = { 5438c2ecf20Sopenharmony_ci &tegra_host1x_driver, 5448c2ecf20Sopenharmony_ci &tegra_mipi_driver, 5458c2ecf20Sopenharmony_ci}; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_cistatic int __init tegra_host1x_init(void) 5488c2ecf20Sopenharmony_ci{ 5498c2ecf20Sopenharmony_ci int err; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci err = bus_register(&host1x_bus_type); 5528c2ecf20Sopenharmony_ci if (err < 0) 5538c2ecf20Sopenharmony_ci return err; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci err = platform_register_drivers(drivers, ARRAY_SIZE(drivers)); 5568c2ecf20Sopenharmony_ci if (err < 0) 5578c2ecf20Sopenharmony_ci bus_unregister(&host1x_bus_type); 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci return err; 5608c2ecf20Sopenharmony_ci} 5618c2ecf20Sopenharmony_cimodule_init(tegra_host1x_init); 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_cistatic void __exit tegra_host1x_exit(void) 5648c2ecf20Sopenharmony_ci{ 5658c2ecf20Sopenharmony_ci platform_unregister_drivers(drivers, ARRAY_SIZE(drivers)); 5668c2ecf20Sopenharmony_ci bus_unregister(&host1x_bus_type); 5678c2ecf20Sopenharmony_ci} 5688c2ecf20Sopenharmony_cimodule_exit(tegra_host1x_exit); 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci/** 5718c2ecf20Sopenharmony_ci * host1x_get_dma_mask() - query the supported DMA mask for host1x 5728c2ecf20Sopenharmony_ci * @host1x: host1x instance 5738c2ecf20Sopenharmony_ci * 5748c2ecf20Sopenharmony_ci * Note that this returns the supported DMA mask for host1x, which can be 5758c2ecf20Sopenharmony_ci * different from the applicable DMA mask under certain circumstances. 5768c2ecf20Sopenharmony_ci */ 5778c2ecf20Sopenharmony_ciu64 host1x_get_dma_mask(struct host1x *host1x) 5788c2ecf20Sopenharmony_ci{ 5798c2ecf20Sopenharmony_ci return host1x->info->dma_mask; 5808c2ecf20Sopenharmony_ci} 5818c2ecf20Sopenharmony_ciEXPORT_SYMBOL(host1x_get_dma_mask); 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ciMODULE_AUTHOR("Thierry Reding <thierry.reding@avionic-design.de>"); 5848c2ecf20Sopenharmony_ciMODULE_AUTHOR("Terje Bergstrom <tbergstrom@nvidia.com>"); 5858c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Host1x driver for Tegra products"); 5868c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 587