18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Author: Stepan Moskovchenko <stepanm@codeaurora.org> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 88c2ecf20Sopenharmony_ci#include <linux/kernel.h> 98c2ecf20Sopenharmony_ci#include <linux/init.h> 108c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 118c2ecf20Sopenharmony_ci#include <linux/errno.h> 128c2ecf20Sopenharmony_ci#include <linux/io.h> 138c2ecf20Sopenharmony_ci#include <linux/io-pgtable.h> 148c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 158c2ecf20Sopenharmony_ci#include <linux/list.h> 168c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 178c2ecf20Sopenharmony_ci#include <linux/slab.h> 188c2ecf20Sopenharmony_ci#include <linux/iommu.h> 198c2ecf20Sopenharmony_ci#include <linux/clk.h> 208c2ecf20Sopenharmony_ci#include <linux/err.h> 218c2ecf20Sopenharmony_ci#include <linux/of_iommu.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include <asm/cacheflush.h> 248c2ecf20Sopenharmony_ci#include <linux/sizes.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include "msm_iommu_hw-8xxx.h" 278c2ecf20Sopenharmony_ci#include "msm_iommu.h" 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define MRC(reg, processor, op1, crn, crm, op2) \ 308c2ecf20Sopenharmony_ci__asm__ __volatile__ ( \ 318c2ecf20Sopenharmony_ci" mrc " #processor "," #op1 ", %0," #crn "," #crm "," #op2 "\n" \ 328c2ecf20Sopenharmony_ci: "=r" (reg)) 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* bitmap of the page sizes currently supported */ 358c2ecf20Sopenharmony_ci#define MSM_IOMMU_PGSIZES (SZ_4K | SZ_64K | SZ_1M | SZ_16M) 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(msm_iommu_lock); 388c2ecf20Sopenharmony_cistatic LIST_HEAD(qcom_iommu_devices); 398c2ecf20Sopenharmony_cistatic struct iommu_ops msm_iommu_ops; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistruct msm_priv { 428c2ecf20Sopenharmony_ci struct list_head list_attached; 438c2ecf20Sopenharmony_ci struct iommu_domain domain; 448c2ecf20Sopenharmony_ci struct io_pgtable_cfg cfg; 458c2ecf20Sopenharmony_ci struct io_pgtable_ops *iop; 468c2ecf20Sopenharmony_ci struct device *dev; 478c2ecf20Sopenharmony_ci spinlock_t pgtlock; /* pagetable lock */ 488c2ecf20Sopenharmony_ci}; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic struct msm_priv *to_msm_priv(struct iommu_domain *dom) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci return container_of(dom, struct msm_priv, domain); 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic int __enable_clocks(struct msm_iommu_dev *iommu) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci int ret; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci ret = clk_enable(iommu->pclk); 608c2ecf20Sopenharmony_ci if (ret) 618c2ecf20Sopenharmony_ci goto fail; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci if (iommu->clk) { 648c2ecf20Sopenharmony_ci ret = clk_enable(iommu->clk); 658c2ecf20Sopenharmony_ci if (ret) 668c2ecf20Sopenharmony_ci clk_disable(iommu->pclk); 678c2ecf20Sopenharmony_ci } 688c2ecf20Sopenharmony_cifail: 698c2ecf20Sopenharmony_ci return ret; 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic void __disable_clocks(struct msm_iommu_dev *iommu) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci if (iommu->clk) 758c2ecf20Sopenharmony_ci clk_disable(iommu->clk); 768c2ecf20Sopenharmony_ci clk_disable(iommu->pclk); 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic void msm_iommu_reset(void __iomem *base, int ncb) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci int ctx; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci SET_RPUE(base, 0); 848c2ecf20Sopenharmony_ci SET_RPUEIE(base, 0); 858c2ecf20Sopenharmony_ci SET_ESRRESTORE(base, 0); 868c2ecf20Sopenharmony_ci SET_TBE(base, 0); 878c2ecf20Sopenharmony_ci SET_CR(base, 0); 888c2ecf20Sopenharmony_ci SET_SPDMBE(base, 0); 898c2ecf20Sopenharmony_ci SET_TESTBUSCR(base, 0); 908c2ecf20Sopenharmony_ci SET_TLBRSW(base, 0); 918c2ecf20Sopenharmony_ci SET_GLOBAL_TLBIALL(base, 0); 928c2ecf20Sopenharmony_ci SET_RPU_ACR(base, 0); 938c2ecf20Sopenharmony_ci SET_TLBLKCRWE(base, 1); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci for (ctx = 0; ctx < ncb; ctx++) { 968c2ecf20Sopenharmony_ci SET_BPRCOSH(base, ctx, 0); 978c2ecf20Sopenharmony_ci SET_BPRCISH(base, ctx, 0); 988c2ecf20Sopenharmony_ci SET_BPRCNSH(base, ctx, 0); 998c2ecf20Sopenharmony_ci SET_BPSHCFG(base, ctx, 0); 1008c2ecf20Sopenharmony_ci SET_BPMTCFG(base, ctx, 0); 1018c2ecf20Sopenharmony_ci SET_ACTLR(base, ctx, 0); 1028c2ecf20Sopenharmony_ci SET_SCTLR(base, ctx, 0); 1038c2ecf20Sopenharmony_ci SET_FSRRESTORE(base, ctx, 0); 1048c2ecf20Sopenharmony_ci SET_TTBR0(base, ctx, 0); 1058c2ecf20Sopenharmony_ci SET_TTBR1(base, ctx, 0); 1068c2ecf20Sopenharmony_ci SET_TTBCR(base, ctx, 0); 1078c2ecf20Sopenharmony_ci SET_BFBCR(base, ctx, 0); 1088c2ecf20Sopenharmony_ci SET_PAR(base, ctx, 0); 1098c2ecf20Sopenharmony_ci SET_FAR(base, ctx, 0); 1108c2ecf20Sopenharmony_ci SET_CTX_TLBIALL(base, ctx, 0); 1118c2ecf20Sopenharmony_ci SET_TLBFLPTER(base, ctx, 0); 1128c2ecf20Sopenharmony_ci SET_TLBSLPTER(base, ctx, 0); 1138c2ecf20Sopenharmony_ci SET_TLBLKCR(base, ctx, 0); 1148c2ecf20Sopenharmony_ci SET_CONTEXTIDR(base, ctx, 0); 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic void __flush_iotlb(void *cookie) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci struct msm_priv *priv = cookie; 1218c2ecf20Sopenharmony_ci struct msm_iommu_dev *iommu = NULL; 1228c2ecf20Sopenharmony_ci struct msm_iommu_ctx_dev *master; 1238c2ecf20Sopenharmony_ci int ret = 0; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci list_for_each_entry(iommu, &priv->list_attached, dom_node) { 1268c2ecf20Sopenharmony_ci ret = __enable_clocks(iommu); 1278c2ecf20Sopenharmony_ci if (ret) 1288c2ecf20Sopenharmony_ci goto fail; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci list_for_each_entry(master, &iommu->ctx_list, list) 1318c2ecf20Sopenharmony_ci SET_CTX_TLBIALL(iommu->base, master->num, 0); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci __disable_clocks(iommu); 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_cifail: 1368c2ecf20Sopenharmony_ci return; 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic void __flush_iotlb_range(unsigned long iova, size_t size, 1408c2ecf20Sopenharmony_ci size_t granule, bool leaf, void *cookie) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci struct msm_priv *priv = cookie; 1438c2ecf20Sopenharmony_ci struct msm_iommu_dev *iommu = NULL; 1448c2ecf20Sopenharmony_ci struct msm_iommu_ctx_dev *master; 1458c2ecf20Sopenharmony_ci int ret = 0; 1468c2ecf20Sopenharmony_ci int temp_size; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci list_for_each_entry(iommu, &priv->list_attached, dom_node) { 1498c2ecf20Sopenharmony_ci ret = __enable_clocks(iommu); 1508c2ecf20Sopenharmony_ci if (ret) 1518c2ecf20Sopenharmony_ci goto fail; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci list_for_each_entry(master, &iommu->ctx_list, list) { 1548c2ecf20Sopenharmony_ci temp_size = size; 1558c2ecf20Sopenharmony_ci do { 1568c2ecf20Sopenharmony_ci iova &= TLBIVA_VA; 1578c2ecf20Sopenharmony_ci iova |= GET_CONTEXTIDR_ASID(iommu->base, 1588c2ecf20Sopenharmony_ci master->num); 1598c2ecf20Sopenharmony_ci SET_TLBIVA(iommu->base, master->num, iova); 1608c2ecf20Sopenharmony_ci iova += granule; 1618c2ecf20Sopenharmony_ci } while (temp_size -= granule); 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci __disable_clocks(iommu); 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cifail: 1688c2ecf20Sopenharmony_ci return; 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic void __flush_iotlb_walk(unsigned long iova, size_t size, 1728c2ecf20Sopenharmony_ci size_t granule, void *cookie) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci __flush_iotlb_range(iova, size, granule, false, cookie); 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic void __flush_iotlb_leaf(unsigned long iova, size_t size, 1788c2ecf20Sopenharmony_ci size_t granule, void *cookie) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci __flush_iotlb_range(iova, size, granule, true, cookie); 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic void __flush_iotlb_page(struct iommu_iotlb_gather *gather, 1848c2ecf20Sopenharmony_ci unsigned long iova, size_t granule, void *cookie) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci __flush_iotlb_range(iova, granule, granule, true, cookie); 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic const struct iommu_flush_ops msm_iommu_flush_ops = { 1908c2ecf20Sopenharmony_ci .tlb_flush_all = __flush_iotlb, 1918c2ecf20Sopenharmony_ci .tlb_flush_walk = __flush_iotlb_walk, 1928c2ecf20Sopenharmony_ci .tlb_flush_leaf = __flush_iotlb_leaf, 1938c2ecf20Sopenharmony_ci .tlb_add_page = __flush_iotlb_page, 1948c2ecf20Sopenharmony_ci}; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic int msm_iommu_alloc_ctx(unsigned long *map, int start, int end) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci int idx; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci do { 2018c2ecf20Sopenharmony_ci idx = find_next_zero_bit(map, end, start); 2028c2ecf20Sopenharmony_ci if (idx == end) 2038c2ecf20Sopenharmony_ci return -ENOSPC; 2048c2ecf20Sopenharmony_ci } while (test_and_set_bit(idx, map)); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci return idx; 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic void msm_iommu_free_ctx(unsigned long *map, int idx) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci clear_bit(idx, map); 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cistatic void config_mids(struct msm_iommu_dev *iommu, 2158c2ecf20Sopenharmony_ci struct msm_iommu_ctx_dev *master) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci int mid, ctx, i; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci for (i = 0; i < master->num_mids; i++) { 2208c2ecf20Sopenharmony_ci mid = master->mids[i]; 2218c2ecf20Sopenharmony_ci ctx = master->num; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci SET_M2VCBR_N(iommu->base, mid, 0); 2248c2ecf20Sopenharmony_ci SET_CBACR_N(iommu->base, ctx, 0); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci /* Set VMID = 0 */ 2278c2ecf20Sopenharmony_ci SET_VMID(iommu->base, mid, 0); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci /* Set the context number for that MID to this context */ 2308c2ecf20Sopenharmony_ci SET_CBNDX(iommu->base, mid, ctx); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci /* Set MID associated with this context bank to 0*/ 2338c2ecf20Sopenharmony_ci SET_CBVMID(iommu->base, ctx, 0); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci /* Set the ASID for TLB tagging for this context */ 2368c2ecf20Sopenharmony_ci SET_CONTEXTIDR_ASID(iommu->base, ctx, ctx); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci /* Set security bit override to be Non-secure */ 2398c2ecf20Sopenharmony_ci SET_NSCFG(iommu->base, mid, 3); 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_cistatic void __reset_context(void __iomem *base, int ctx) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci SET_BPRCOSH(base, ctx, 0); 2468c2ecf20Sopenharmony_ci SET_BPRCISH(base, ctx, 0); 2478c2ecf20Sopenharmony_ci SET_BPRCNSH(base, ctx, 0); 2488c2ecf20Sopenharmony_ci SET_BPSHCFG(base, ctx, 0); 2498c2ecf20Sopenharmony_ci SET_BPMTCFG(base, ctx, 0); 2508c2ecf20Sopenharmony_ci SET_ACTLR(base, ctx, 0); 2518c2ecf20Sopenharmony_ci SET_SCTLR(base, ctx, 0); 2528c2ecf20Sopenharmony_ci SET_FSRRESTORE(base, ctx, 0); 2538c2ecf20Sopenharmony_ci SET_TTBR0(base, ctx, 0); 2548c2ecf20Sopenharmony_ci SET_TTBR1(base, ctx, 0); 2558c2ecf20Sopenharmony_ci SET_TTBCR(base, ctx, 0); 2568c2ecf20Sopenharmony_ci SET_BFBCR(base, ctx, 0); 2578c2ecf20Sopenharmony_ci SET_PAR(base, ctx, 0); 2588c2ecf20Sopenharmony_ci SET_FAR(base, ctx, 0); 2598c2ecf20Sopenharmony_ci SET_CTX_TLBIALL(base, ctx, 0); 2608c2ecf20Sopenharmony_ci SET_TLBFLPTER(base, ctx, 0); 2618c2ecf20Sopenharmony_ci SET_TLBSLPTER(base, ctx, 0); 2628c2ecf20Sopenharmony_ci SET_TLBLKCR(base, ctx, 0); 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic void __program_context(void __iomem *base, int ctx, 2668c2ecf20Sopenharmony_ci struct msm_priv *priv) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci __reset_context(base, ctx); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci /* Turn on TEX Remap */ 2718c2ecf20Sopenharmony_ci SET_TRE(base, ctx, 1); 2728c2ecf20Sopenharmony_ci SET_AFE(base, ctx, 1); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci /* Set up HTW mode */ 2758c2ecf20Sopenharmony_ci /* TLB miss configuration: perform HTW on miss */ 2768c2ecf20Sopenharmony_ci SET_TLBMCFG(base, ctx, 0x3); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci /* V2P configuration: HTW for access */ 2798c2ecf20Sopenharmony_ci SET_V2PCFG(base, ctx, 0x3); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci SET_TTBCR(base, ctx, priv->cfg.arm_v7s_cfg.tcr); 2828c2ecf20Sopenharmony_ci SET_TTBR0(base, ctx, priv->cfg.arm_v7s_cfg.ttbr); 2838c2ecf20Sopenharmony_ci SET_TTBR1(base, ctx, 0); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci /* Set prrr and nmrr */ 2868c2ecf20Sopenharmony_ci SET_PRRR(base, ctx, priv->cfg.arm_v7s_cfg.prrr); 2878c2ecf20Sopenharmony_ci SET_NMRR(base, ctx, priv->cfg.arm_v7s_cfg.nmrr); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci /* Invalidate the TLB for this context */ 2908c2ecf20Sopenharmony_ci SET_CTX_TLBIALL(base, ctx, 0); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci /* Set interrupt number to "secure" interrupt */ 2938c2ecf20Sopenharmony_ci SET_IRPTNDX(base, ctx, 0); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci /* Enable context fault interrupt */ 2968c2ecf20Sopenharmony_ci SET_CFEIE(base, ctx, 1); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci /* Stall access on a context fault and let the handler deal with it */ 2998c2ecf20Sopenharmony_ci SET_CFCFG(base, ctx, 1); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci /* Redirect all cacheable requests to L2 slave port. */ 3028c2ecf20Sopenharmony_ci SET_RCISH(base, ctx, 1); 3038c2ecf20Sopenharmony_ci SET_RCOSH(base, ctx, 1); 3048c2ecf20Sopenharmony_ci SET_RCNSH(base, ctx, 1); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci /* Turn on BFB prefetch */ 3078c2ecf20Sopenharmony_ci SET_BFBDFE(base, ctx, 1); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci /* Enable the MMU */ 3108c2ecf20Sopenharmony_ci SET_M(base, ctx, 1); 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_cistatic struct iommu_domain *msm_iommu_domain_alloc(unsigned type) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci struct msm_priv *priv; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci if (type != IOMMU_DOMAIN_UNMANAGED) 3188c2ecf20Sopenharmony_ci return NULL; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci priv = kzalloc(sizeof(*priv), GFP_KERNEL); 3218c2ecf20Sopenharmony_ci if (!priv) 3228c2ecf20Sopenharmony_ci goto fail_nomem; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&priv->list_attached); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci priv->domain.geometry.aperture_start = 0; 3278c2ecf20Sopenharmony_ci priv->domain.geometry.aperture_end = (1ULL << 32) - 1; 3288c2ecf20Sopenharmony_ci priv->domain.geometry.force_aperture = true; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci return &priv->domain; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_cifail_nomem: 3338c2ecf20Sopenharmony_ci kfree(priv); 3348c2ecf20Sopenharmony_ci return NULL; 3358c2ecf20Sopenharmony_ci} 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_cistatic void msm_iommu_domain_free(struct iommu_domain *domain) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci struct msm_priv *priv; 3408c2ecf20Sopenharmony_ci unsigned long flags; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci spin_lock_irqsave(&msm_iommu_lock, flags); 3438c2ecf20Sopenharmony_ci priv = to_msm_priv(domain); 3448c2ecf20Sopenharmony_ci kfree(priv); 3458c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&msm_iommu_lock, flags); 3468c2ecf20Sopenharmony_ci} 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_cistatic int msm_iommu_domain_config(struct msm_priv *priv) 3498c2ecf20Sopenharmony_ci{ 3508c2ecf20Sopenharmony_ci spin_lock_init(&priv->pgtlock); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci priv->cfg = (struct io_pgtable_cfg) { 3538c2ecf20Sopenharmony_ci .quirks = IO_PGTABLE_QUIRK_TLBI_ON_MAP, 3548c2ecf20Sopenharmony_ci .pgsize_bitmap = msm_iommu_ops.pgsize_bitmap, 3558c2ecf20Sopenharmony_ci .ias = 32, 3568c2ecf20Sopenharmony_ci .oas = 32, 3578c2ecf20Sopenharmony_ci .tlb = &msm_iommu_flush_ops, 3588c2ecf20Sopenharmony_ci .iommu_dev = priv->dev, 3598c2ecf20Sopenharmony_ci }; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci priv->iop = alloc_io_pgtable_ops(ARM_V7S, &priv->cfg, priv); 3628c2ecf20Sopenharmony_ci if (!priv->iop) { 3638c2ecf20Sopenharmony_ci dev_err(priv->dev, "Failed to allocate pgtable\n"); 3648c2ecf20Sopenharmony_ci return -EINVAL; 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci msm_iommu_ops.pgsize_bitmap = priv->cfg.pgsize_bitmap; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci return 0; 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci/* Must be called under msm_iommu_lock */ 3738c2ecf20Sopenharmony_cistatic struct msm_iommu_dev *find_iommu_for_dev(struct device *dev) 3748c2ecf20Sopenharmony_ci{ 3758c2ecf20Sopenharmony_ci struct msm_iommu_dev *iommu, *ret = NULL; 3768c2ecf20Sopenharmony_ci struct msm_iommu_ctx_dev *master; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci list_for_each_entry(iommu, &qcom_iommu_devices, dev_node) { 3798c2ecf20Sopenharmony_ci master = list_first_entry(&iommu->ctx_list, 3808c2ecf20Sopenharmony_ci struct msm_iommu_ctx_dev, 3818c2ecf20Sopenharmony_ci list); 3828c2ecf20Sopenharmony_ci if (master->of_node == dev->of_node) { 3838c2ecf20Sopenharmony_ci ret = iommu; 3848c2ecf20Sopenharmony_ci break; 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci return ret; 3898c2ecf20Sopenharmony_ci} 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_cistatic struct iommu_device *msm_iommu_probe_device(struct device *dev) 3928c2ecf20Sopenharmony_ci{ 3938c2ecf20Sopenharmony_ci struct msm_iommu_dev *iommu; 3948c2ecf20Sopenharmony_ci unsigned long flags; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci spin_lock_irqsave(&msm_iommu_lock, flags); 3978c2ecf20Sopenharmony_ci iommu = find_iommu_for_dev(dev); 3988c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&msm_iommu_lock, flags); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci if (!iommu) 4018c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci return &iommu->iommu; 4048c2ecf20Sopenharmony_ci} 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_cistatic void msm_iommu_release_device(struct device *dev) 4078c2ecf20Sopenharmony_ci{ 4088c2ecf20Sopenharmony_ci} 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_cistatic int msm_iommu_attach_dev(struct iommu_domain *domain, struct device *dev) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci int ret = 0; 4138c2ecf20Sopenharmony_ci unsigned long flags; 4148c2ecf20Sopenharmony_ci struct msm_iommu_dev *iommu; 4158c2ecf20Sopenharmony_ci struct msm_priv *priv = to_msm_priv(domain); 4168c2ecf20Sopenharmony_ci struct msm_iommu_ctx_dev *master; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci priv->dev = dev; 4198c2ecf20Sopenharmony_ci msm_iommu_domain_config(priv); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci spin_lock_irqsave(&msm_iommu_lock, flags); 4228c2ecf20Sopenharmony_ci list_for_each_entry(iommu, &qcom_iommu_devices, dev_node) { 4238c2ecf20Sopenharmony_ci master = list_first_entry(&iommu->ctx_list, 4248c2ecf20Sopenharmony_ci struct msm_iommu_ctx_dev, 4258c2ecf20Sopenharmony_ci list); 4268c2ecf20Sopenharmony_ci if (master->of_node == dev->of_node) { 4278c2ecf20Sopenharmony_ci ret = __enable_clocks(iommu); 4288c2ecf20Sopenharmony_ci if (ret) 4298c2ecf20Sopenharmony_ci goto fail; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci list_for_each_entry(master, &iommu->ctx_list, list) { 4328c2ecf20Sopenharmony_ci if (master->num) { 4338c2ecf20Sopenharmony_ci dev_err(dev, "domain already attached"); 4348c2ecf20Sopenharmony_ci ret = -EEXIST; 4358c2ecf20Sopenharmony_ci goto fail; 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci master->num = 4388c2ecf20Sopenharmony_ci msm_iommu_alloc_ctx(iommu->context_map, 4398c2ecf20Sopenharmony_ci 0, iommu->ncb); 4408c2ecf20Sopenharmony_ci if (IS_ERR_VALUE(master->num)) { 4418c2ecf20Sopenharmony_ci ret = -ENODEV; 4428c2ecf20Sopenharmony_ci goto fail; 4438c2ecf20Sopenharmony_ci } 4448c2ecf20Sopenharmony_ci config_mids(iommu, master); 4458c2ecf20Sopenharmony_ci __program_context(iommu->base, master->num, 4468c2ecf20Sopenharmony_ci priv); 4478c2ecf20Sopenharmony_ci } 4488c2ecf20Sopenharmony_ci __disable_clocks(iommu); 4498c2ecf20Sopenharmony_ci list_add(&iommu->dom_node, &priv->list_attached); 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci } 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_cifail: 4548c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&msm_iommu_lock, flags); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci return ret; 4578c2ecf20Sopenharmony_ci} 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_cistatic void msm_iommu_detach_dev(struct iommu_domain *domain, 4608c2ecf20Sopenharmony_ci struct device *dev) 4618c2ecf20Sopenharmony_ci{ 4628c2ecf20Sopenharmony_ci struct msm_priv *priv = to_msm_priv(domain); 4638c2ecf20Sopenharmony_ci unsigned long flags; 4648c2ecf20Sopenharmony_ci struct msm_iommu_dev *iommu; 4658c2ecf20Sopenharmony_ci struct msm_iommu_ctx_dev *master; 4668c2ecf20Sopenharmony_ci int ret; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci free_io_pgtable_ops(priv->iop); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci spin_lock_irqsave(&msm_iommu_lock, flags); 4718c2ecf20Sopenharmony_ci list_for_each_entry(iommu, &priv->list_attached, dom_node) { 4728c2ecf20Sopenharmony_ci ret = __enable_clocks(iommu); 4738c2ecf20Sopenharmony_ci if (ret) 4748c2ecf20Sopenharmony_ci goto fail; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci list_for_each_entry(master, &iommu->ctx_list, list) { 4778c2ecf20Sopenharmony_ci msm_iommu_free_ctx(iommu->context_map, master->num); 4788c2ecf20Sopenharmony_ci __reset_context(iommu->base, master->num); 4798c2ecf20Sopenharmony_ci } 4808c2ecf20Sopenharmony_ci __disable_clocks(iommu); 4818c2ecf20Sopenharmony_ci } 4828c2ecf20Sopenharmony_cifail: 4838c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&msm_iommu_lock, flags); 4848c2ecf20Sopenharmony_ci} 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_cistatic int msm_iommu_map(struct iommu_domain *domain, unsigned long iova, 4878c2ecf20Sopenharmony_ci phys_addr_t pa, size_t len, int prot, gfp_t gfp) 4888c2ecf20Sopenharmony_ci{ 4898c2ecf20Sopenharmony_ci struct msm_priv *priv = to_msm_priv(domain); 4908c2ecf20Sopenharmony_ci unsigned long flags; 4918c2ecf20Sopenharmony_ci int ret; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->pgtlock, flags); 4948c2ecf20Sopenharmony_ci ret = priv->iop->map(priv->iop, iova, pa, len, prot, GFP_ATOMIC); 4958c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->pgtlock, flags); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci return ret; 4988c2ecf20Sopenharmony_ci} 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_cistatic size_t msm_iommu_unmap(struct iommu_domain *domain, unsigned long iova, 5018c2ecf20Sopenharmony_ci size_t len, struct iommu_iotlb_gather *gather) 5028c2ecf20Sopenharmony_ci{ 5038c2ecf20Sopenharmony_ci struct msm_priv *priv = to_msm_priv(domain); 5048c2ecf20Sopenharmony_ci unsigned long flags; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->pgtlock, flags); 5078c2ecf20Sopenharmony_ci len = priv->iop->unmap(priv->iop, iova, len, gather); 5088c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->pgtlock, flags); 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci return len; 5118c2ecf20Sopenharmony_ci} 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_cistatic phys_addr_t msm_iommu_iova_to_phys(struct iommu_domain *domain, 5148c2ecf20Sopenharmony_ci dma_addr_t va) 5158c2ecf20Sopenharmony_ci{ 5168c2ecf20Sopenharmony_ci struct msm_priv *priv; 5178c2ecf20Sopenharmony_ci struct msm_iommu_dev *iommu; 5188c2ecf20Sopenharmony_ci struct msm_iommu_ctx_dev *master; 5198c2ecf20Sopenharmony_ci unsigned int par; 5208c2ecf20Sopenharmony_ci unsigned long flags; 5218c2ecf20Sopenharmony_ci phys_addr_t ret = 0; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci spin_lock_irqsave(&msm_iommu_lock, flags); 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci priv = to_msm_priv(domain); 5268c2ecf20Sopenharmony_ci iommu = list_first_entry(&priv->list_attached, 5278c2ecf20Sopenharmony_ci struct msm_iommu_dev, dom_node); 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci if (list_empty(&iommu->ctx_list)) 5308c2ecf20Sopenharmony_ci goto fail; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci master = list_first_entry(&iommu->ctx_list, 5338c2ecf20Sopenharmony_ci struct msm_iommu_ctx_dev, list); 5348c2ecf20Sopenharmony_ci if (!master) 5358c2ecf20Sopenharmony_ci goto fail; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci ret = __enable_clocks(iommu); 5388c2ecf20Sopenharmony_ci if (ret) 5398c2ecf20Sopenharmony_ci goto fail; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci /* Invalidate context TLB */ 5428c2ecf20Sopenharmony_ci SET_CTX_TLBIALL(iommu->base, master->num, 0); 5438c2ecf20Sopenharmony_ci SET_V2PPR(iommu->base, master->num, va & V2Pxx_VA); 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci par = GET_PAR(iommu->base, master->num); 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci /* We are dealing with a supersection */ 5488c2ecf20Sopenharmony_ci if (GET_NOFAULT_SS(iommu->base, master->num)) 5498c2ecf20Sopenharmony_ci ret = (par & 0xFF000000) | (va & 0x00FFFFFF); 5508c2ecf20Sopenharmony_ci else /* Upper 20 bits from PAR, lower 12 from VA */ 5518c2ecf20Sopenharmony_ci ret = (par & 0xFFFFF000) | (va & 0x00000FFF); 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci if (GET_FAULT(iommu->base, master->num)) 5548c2ecf20Sopenharmony_ci ret = 0; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci __disable_clocks(iommu); 5578c2ecf20Sopenharmony_cifail: 5588c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&msm_iommu_lock, flags); 5598c2ecf20Sopenharmony_ci return ret; 5608c2ecf20Sopenharmony_ci} 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_cistatic bool msm_iommu_capable(enum iommu_cap cap) 5638c2ecf20Sopenharmony_ci{ 5648c2ecf20Sopenharmony_ci return false; 5658c2ecf20Sopenharmony_ci} 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_cistatic void print_ctx_regs(void __iomem *base, int ctx) 5688c2ecf20Sopenharmony_ci{ 5698c2ecf20Sopenharmony_ci unsigned int fsr = GET_FSR(base, ctx); 5708c2ecf20Sopenharmony_ci pr_err("FAR = %08x PAR = %08x\n", 5718c2ecf20Sopenharmony_ci GET_FAR(base, ctx), GET_PAR(base, ctx)); 5728c2ecf20Sopenharmony_ci pr_err("FSR = %08x [%s%s%s%s%s%s%s%s%s%s]\n", fsr, 5738c2ecf20Sopenharmony_ci (fsr & 0x02) ? "TF " : "", 5748c2ecf20Sopenharmony_ci (fsr & 0x04) ? "AFF " : "", 5758c2ecf20Sopenharmony_ci (fsr & 0x08) ? "APF " : "", 5768c2ecf20Sopenharmony_ci (fsr & 0x10) ? "TLBMF " : "", 5778c2ecf20Sopenharmony_ci (fsr & 0x20) ? "HTWDEEF " : "", 5788c2ecf20Sopenharmony_ci (fsr & 0x40) ? "HTWSEEF " : "", 5798c2ecf20Sopenharmony_ci (fsr & 0x80) ? "MHF " : "", 5808c2ecf20Sopenharmony_ci (fsr & 0x10000) ? "SL " : "", 5818c2ecf20Sopenharmony_ci (fsr & 0x40000000) ? "SS " : "", 5828c2ecf20Sopenharmony_ci (fsr & 0x80000000) ? "MULTI " : ""); 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci pr_err("FSYNR0 = %08x FSYNR1 = %08x\n", 5858c2ecf20Sopenharmony_ci GET_FSYNR0(base, ctx), GET_FSYNR1(base, ctx)); 5868c2ecf20Sopenharmony_ci pr_err("TTBR0 = %08x TTBR1 = %08x\n", 5878c2ecf20Sopenharmony_ci GET_TTBR0(base, ctx), GET_TTBR1(base, ctx)); 5888c2ecf20Sopenharmony_ci pr_err("SCTLR = %08x ACTLR = %08x\n", 5898c2ecf20Sopenharmony_ci GET_SCTLR(base, ctx), GET_ACTLR(base, ctx)); 5908c2ecf20Sopenharmony_ci} 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_cistatic void insert_iommu_master(struct device *dev, 5938c2ecf20Sopenharmony_ci struct msm_iommu_dev **iommu, 5948c2ecf20Sopenharmony_ci struct of_phandle_args *spec) 5958c2ecf20Sopenharmony_ci{ 5968c2ecf20Sopenharmony_ci struct msm_iommu_ctx_dev *master = dev_iommu_priv_get(dev); 5978c2ecf20Sopenharmony_ci int sid; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci if (list_empty(&(*iommu)->ctx_list)) { 6008c2ecf20Sopenharmony_ci master = kzalloc(sizeof(*master), GFP_ATOMIC); 6018c2ecf20Sopenharmony_ci master->of_node = dev->of_node; 6028c2ecf20Sopenharmony_ci list_add(&master->list, &(*iommu)->ctx_list); 6038c2ecf20Sopenharmony_ci dev_iommu_priv_set(dev, master); 6048c2ecf20Sopenharmony_ci } 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci for (sid = 0; sid < master->num_mids; sid++) 6078c2ecf20Sopenharmony_ci if (master->mids[sid] == spec->args[0]) { 6088c2ecf20Sopenharmony_ci dev_warn(dev, "Stream ID 0x%hx repeated; ignoring\n", 6098c2ecf20Sopenharmony_ci sid); 6108c2ecf20Sopenharmony_ci return; 6118c2ecf20Sopenharmony_ci } 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci master->mids[master->num_mids++] = spec->args[0]; 6148c2ecf20Sopenharmony_ci} 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_cistatic int qcom_iommu_of_xlate(struct device *dev, 6178c2ecf20Sopenharmony_ci struct of_phandle_args *spec) 6188c2ecf20Sopenharmony_ci{ 6198c2ecf20Sopenharmony_ci struct msm_iommu_dev *iommu = NULL, *iter; 6208c2ecf20Sopenharmony_ci unsigned long flags; 6218c2ecf20Sopenharmony_ci int ret = 0; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci spin_lock_irqsave(&msm_iommu_lock, flags); 6248c2ecf20Sopenharmony_ci list_for_each_entry(iter, &qcom_iommu_devices, dev_node) { 6258c2ecf20Sopenharmony_ci if (iter->dev->of_node == spec->np) { 6268c2ecf20Sopenharmony_ci iommu = iter; 6278c2ecf20Sopenharmony_ci break; 6288c2ecf20Sopenharmony_ci } 6298c2ecf20Sopenharmony_ci } 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci if (!iommu) { 6328c2ecf20Sopenharmony_ci ret = -ENODEV; 6338c2ecf20Sopenharmony_ci goto fail; 6348c2ecf20Sopenharmony_ci } 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci insert_iommu_master(dev, &iommu, spec); 6378c2ecf20Sopenharmony_cifail: 6388c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&msm_iommu_lock, flags); 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci return ret; 6418c2ecf20Sopenharmony_ci} 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ciirqreturn_t msm_iommu_fault_handler(int irq, void *dev_id) 6448c2ecf20Sopenharmony_ci{ 6458c2ecf20Sopenharmony_ci struct msm_iommu_dev *iommu = dev_id; 6468c2ecf20Sopenharmony_ci unsigned int fsr; 6478c2ecf20Sopenharmony_ci int i, ret; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci spin_lock(&msm_iommu_lock); 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci if (!iommu) { 6528c2ecf20Sopenharmony_ci pr_err("Invalid device ID in context interrupt handler\n"); 6538c2ecf20Sopenharmony_ci goto fail; 6548c2ecf20Sopenharmony_ci } 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci pr_err("Unexpected IOMMU page fault!\n"); 6578c2ecf20Sopenharmony_ci pr_err("base = %08x\n", (unsigned int)iommu->base); 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci ret = __enable_clocks(iommu); 6608c2ecf20Sopenharmony_ci if (ret) 6618c2ecf20Sopenharmony_ci goto fail; 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci for (i = 0; i < iommu->ncb; i++) { 6648c2ecf20Sopenharmony_ci fsr = GET_FSR(iommu->base, i); 6658c2ecf20Sopenharmony_ci if (fsr) { 6668c2ecf20Sopenharmony_ci pr_err("Fault occurred in context %d.\n", i); 6678c2ecf20Sopenharmony_ci pr_err("Interesting registers:\n"); 6688c2ecf20Sopenharmony_ci print_ctx_regs(iommu->base, i); 6698c2ecf20Sopenharmony_ci SET_FSR(iommu->base, i, 0x4000000F); 6708c2ecf20Sopenharmony_ci } 6718c2ecf20Sopenharmony_ci } 6728c2ecf20Sopenharmony_ci __disable_clocks(iommu); 6738c2ecf20Sopenharmony_cifail: 6748c2ecf20Sopenharmony_ci spin_unlock(&msm_iommu_lock); 6758c2ecf20Sopenharmony_ci return 0; 6768c2ecf20Sopenharmony_ci} 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_cistatic struct iommu_ops msm_iommu_ops = { 6798c2ecf20Sopenharmony_ci .capable = msm_iommu_capable, 6808c2ecf20Sopenharmony_ci .domain_alloc = msm_iommu_domain_alloc, 6818c2ecf20Sopenharmony_ci .domain_free = msm_iommu_domain_free, 6828c2ecf20Sopenharmony_ci .attach_dev = msm_iommu_attach_dev, 6838c2ecf20Sopenharmony_ci .detach_dev = msm_iommu_detach_dev, 6848c2ecf20Sopenharmony_ci .map = msm_iommu_map, 6858c2ecf20Sopenharmony_ci .unmap = msm_iommu_unmap, 6868c2ecf20Sopenharmony_ci /* 6878c2ecf20Sopenharmony_ci * Nothing is needed here, the barrier to guarantee 6888c2ecf20Sopenharmony_ci * completion of the tlb sync operation is implicitly 6898c2ecf20Sopenharmony_ci * taken care when the iommu client does a writel before 6908c2ecf20Sopenharmony_ci * kick starting the other master. 6918c2ecf20Sopenharmony_ci */ 6928c2ecf20Sopenharmony_ci .iotlb_sync = NULL, 6938c2ecf20Sopenharmony_ci .iova_to_phys = msm_iommu_iova_to_phys, 6948c2ecf20Sopenharmony_ci .probe_device = msm_iommu_probe_device, 6958c2ecf20Sopenharmony_ci .release_device = msm_iommu_release_device, 6968c2ecf20Sopenharmony_ci .device_group = generic_device_group, 6978c2ecf20Sopenharmony_ci .pgsize_bitmap = MSM_IOMMU_PGSIZES, 6988c2ecf20Sopenharmony_ci .of_xlate = qcom_iommu_of_xlate, 6998c2ecf20Sopenharmony_ci}; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_cistatic int msm_iommu_probe(struct platform_device *pdev) 7028c2ecf20Sopenharmony_ci{ 7038c2ecf20Sopenharmony_ci struct resource *r; 7048c2ecf20Sopenharmony_ci resource_size_t ioaddr; 7058c2ecf20Sopenharmony_ci struct msm_iommu_dev *iommu; 7068c2ecf20Sopenharmony_ci int ret, par, val; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci iommu = devm_kzalloc(&pdev->dev, sizeof(*iommu), GFP_KERNEL); 7098c2ecf20Sopenharmony_ci if (!iommu) 7108c2ecf20Sopenharmony_ci return -ENODEV; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci iommu->dev = &pdev->dev; 7138c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&iommu->ctx_list); 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci iommu->pclk = devm_clk_get(iommu->dev, "smmu_pclk"); 7168c2ecf20Sopenharmony_ci if (IS_ERR(iommu->pclk)) { 7178c2ecf20Sopenharmony_ci dev_err(iommu->dev, "could not get smmu_pclk\n"); 7188c2ecf20Sopenharmony_ci return PTR_ERR(iommu->pclk); 7198c2ecf20Sopenharmony_ci } 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci ret = clk_prepare(iommu->pclk); 7228c2ecf20Sopenharmony_ci if (ret) { 7238c2ecf20Sopenharmony_ci dev_err(iommu->dev, "could not prepare smmu_pclk\n"); 7248c2ecf20Sopenharmony_ci return ret; 7258c2ecf20Sopenharmony_ci } 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci iommu->clk = devm_clk_get(iommu->dev, "iommu_clk"); 7288c2ecf20Sopenharmony_ci if (IS_ERR(iommu->clk)) { 7298c2ecf20Sopenharmony_ci dev_err(iommu->dev, "could not get iommu_clk\n"); 7308c2ecf20Sopenharmony_ci clk_unprepare(iommu->pclk); 7318c2ecf20Sopenharmony_ci return PTR_ERR(iommu->clk); 7328c2ecf20Sopenharmony_ci } 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci ret = clk_prepare(iommu->clk); 7358c2ecf20Sopenharmony_ci if (ret) { 7368c2ecf20Sopenharmony_ci dev_err(iommu->dev, "could not prepare iommu_clk\n"); 7378c2ecf20Sopenharmony_ci clk_unprepare(iommu->pclk); 7388c2ecf20Sopenharmony_ci return ret; 7398c2ecf20Sopenharmony_ci } 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 7428c2ecf20Sopenharmony_ci iommu->base = devm_ioremap_resource(iommu->dev, r); 7438c2ecf20Sopenharmony_ci if (IS_ERR(iommu->base)) { 7448c2ecf20Sopenharmony_ci dev_err(iommu->dev, "could not get iommu base\n"); 7458c2ecf20Sopenharmony_ci ret = PTR_ERR(iommu->base); 7468c2ecf20Sopenharmony_ci goto fail; 7478c2ecf20Sopenharmony_ci } 7488c2ecf20Sopenharmony_ci ioaddr = r->start; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci iommu->irq = platform_get_irq(pdev, 0); 7518c2ecf20Sopenharmony_ci if (iommu->irq < 0) { 7528c2ecf20Sopenharmony_ci ret = -ENODEV; 7538c2ecf20Sopenharmony_ci goto fail; 7548c2ecf20Sopenharmony_ci } 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci ret = of_property_read_u32(iommu->dev->of_node, "qcom,ncb", &val); 7578c2ecf20Sopenharmony_ci if (ret) { 7588c2ecf20Sopenharmony_ci dev_err(iommu->dev, "could not get ncb\n"); 7598c2ecf20Sopenharmony_ci goto fail; 7608c2ecf20Sopenharmony_ci } 7618c2ecf20Sopenharmony_ci iommu->ncb = val; 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci msm_iommu_reset(iommu->base, iommu->ncb); 7648c2ecf20Sopenharmony_ci SET_M(iommu->base, 0, 1); 7658c2ecf20Sopenharmony_ci SET_PAR(iommu->base, 0, 0); 7668c2ecf20Sopenharmony_ci SET_V2PCFG(iommu->base, 0, 1); 7678c2ecf20Sopenharmony_ci SET_V2PPR(iommu->base, 0, 0); 7688c2ecf20Sopenharmony_ci par = GET_PAR(iommu->base, 0); 7698c2ecf20Sopenharmony_ci SET_V2PCFG(iommu->base, 0, 0); 7708c2ecf20Sopenharmony_ci SET_M(iommu->base, 0, 0); 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci if (!par) { 7738c2ecf20Sopenharmony_ci pr_err("Invalid PAR value detected\n"); 7748c2ecf20Sopenharmony_ci ret = -ENODEV; 7758c2ecf20Sopenharmony_ci goto fail; 7768c2ecf20Sopenharmony_ci } 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci ret = devm_request_threaded_irq(iommu->dev, iommu->irq, NULL, 7798c2ecf20Sopenharmony_ci msm_iommu_fault_handler, 7808c2ecf20Sopenharmony_ci IRQF_ONESHOT | IRQF_SHARED, 7818c2ecf20Sopenharmony_ci "msm_iommu_secure_irpt_handler", 7828c2ecf20Sopenharmony_ci iommu); 7838c2ecf20Sopenharmony_ci if (ret) { 7848c2ecf20Sopenharmony_ci pr_err("Request IRQ %d failed with ret=%d\n", iommu->irq, ret); 7858c2ecf20Sopenharmony_ci goto fail; 7868c2ecf20Sopenharmony_ci } 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci list_add(&iommu->dev_node, &qcom_iommu_devices); 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci ret = iommu_device_sysfs_add(&iommu->iommu, iommu->dev, NULL, 7918c2ecf20Sopenharmony_ci "msm-smmu.%pa", &ioaddr); 7928c2ecf20Sopenharmony_ci if (ret) { 7938c2ecf20Sopenharmony_ci pr_err("Could not add msm-smmu at %pa to sysfs\n", &ioaddr); 7948c2ecf20Sopenharmony_ci goto fail; 7958c2ecf20Sopenharmony_ci } 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci iommu_device_set_ops(&iommu->iommu, &msm_iommu_ops); 7988c2ecf20Sopenharmony_ci iommu_device_set_fwnode(&iommu->iommu, &pdev->dev.of_node->fwnode); 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci ret = iommu_device_register(&iommu->iommu); 8018c2ecf20Sopenharmony_ci if (ret) { 8028c2ecf20Sopenharmony_ci pr_err("Could not register msm-smmu at %pa\n", &ioaddr); 8038c2ecf20Sopenharmony_ci goto fail; 8048c2ecf20Sopenharmony_ci } 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci bus_set_iommu(&platform_bus_type, &msm_iommu_ops); 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci pr_info("device mapped at %p, irq %d with %d ctx banks\n", 8098c2ecf20Sopenharmony_ci iommu->base, iommu->irq, iommu->ncb); 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci return ret; 8128c2ecf20Sopenharmony_cifail: 8138c2ecf20Sopenharmony_ci clk_unprepare(iommu->clk); 8148c2ecf20Sopenharmony_ci clk_unprepare(iommu->pclk); 8158c2ecf20Sopenharmony_ci return ret; 8168c2ecf20Sopenharmony_ci} 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_cistatic const struct of_device_id msm_iommu_dt_match[] = { 8198c2ecf20Sopenharmony_ci { .compatible = "qcom,apq8064-iommu" }, 8208c2ecf20Sopenharmony_ci {} 8218c2ecf20Sopenharmony_ci}; 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_cistatic int msm_iommu_remove(struct platform_device *pdev) 8248c2ecf20Sopenharmony_ci{ 8258c2ecf20Sopenharmony_ci struct msm_iommu_dev *iommu = platform_get_drvdata(pdev); 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci clk_unprepare(iommu->clk); 8288c2ecf20Sopenharmony_ci clk_unprepare(iommu->pclk); 8298c2ecf20Sopenharmony_ci return 0; 8308c2ecf20Sopenharmony_ci} 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_cistatic struct platform_driver msm_iommu_driver = { 8338c2ecf20Sopenharmony_ci .driver = { 8348c2ecf20Sopenharmony_ci .name = "msm_iommu", 8358c2ecf20Sopenharmony_ci .of_match_table = msm_iommu_dt_match, 8368c2ecf20Sopenharmony_ci }, 8378c2ecf20Sopenharmony_ci .probe = msm_iommu_probe, 8388c2ecf20Sopenharmony_ci .remove = msm_iommu_remove, 8398c2ecf20Sopenharmony_ci}; 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_cistatic int __init msm_iommu_driver_init(void) 8428c2ecf20Sopenharmony_ci{ 8438c2ecf20Sopenharmony_ci int ret; 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci ret = platform_driver_register(&msm_iommu_driver); 8468c2ecf20Sopenharmony_ci if (ret != 0) 8478c2ecf20Sopenharmony_ci pr_err("Failed to register IOMMU driver\n"); 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci return ret; 8508c2ecf20Sopenharmony_ci} 8518c2ecf20Sopenharmony_cisubsys_initcall(msm_iommu_driver_init); 8528c2ecf20Sopenharmony_ci 853