162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Author: Stepan Moskovchenko <stepanm@codeaurora.org> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/init.h> 1062306a36Sopenharmony_ci#include <linux/platform_device.h> 1162306a36Sopenharmony_ci#include <linux/errno.h> 1262306a36Sopenharmony_ci#include <linux/io.h> 1362306a36Sopenharmony_ci#include <linux/io-pgtable.h> 1462306a36Sopenharmony_ci#include <linux/interrupt.h> 1562306a36Sopenharmony_ci#include <linux/list.h> 1662306a36Sopenharmony_ci#include <linux/spinlock.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci#include <linux/iommu.h> 1962306a36Sopenharmony_ci#include <linux/clk.h> 2062306a36Sopenharmony_ci#include <linux/err.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include <asm/cacheflush.h> 2362306a36Sopenharmony_ci#include <linux/sizes.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include "msm_iommu_hw-8xxx.h" 2662306a36Sopenharmony_ci#include "msm_iommu.h" 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define MRC(reg, processor, op1, crn, crm, op2) \ 2962306a36Sopenharmony_ci__asm__ __volatile__ ( \ 3062306a36Sopenharmony_ci" mrc " #processor "," #op1 ", %0," #crn "," #crm "," #op2 "\n" \ 3162306a36Sopenharmony_ci: "=r" (reg)) 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* bitmap of the page sizes currently supported */ 3462306a36Sopenharmony_ci#define MSM_IOMMU_PGSIZES (SZ_4K | SZ_64K | SZ_1M | SZ_16M) 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic DEFINE_SPINLOCK(msm_iommu_lock); 3762306a36Sopenharmony_cistatic LIST_HEAD(qcom_iommu_devices); 3862306a36Sopenharmony_cistatic struct iommu_ops msm_iommu_ops; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistruct msm_priv { 4162306a36Sopenharmony_ci struct list_head list_attached; 4262306a36Sopenharmony_ci struct iommu_domain domain; 4362306a36Sopenharmony_ci struct io_pgtable_cfg cfg; 4462306a36Sopenharmony_ci struct io_pgtable_ops *iop; 4562306a36Sopenharmony_ci struct device *dev; 4662306a36Sopenharmony_ci spinlock_t pgtlock; /* pagetable lock */ 4762306a36Sopenharmony_ci}; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic struct msm_priv *to_msm_priv(struct iommu_domain *dom) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci return container_of(dom, struct msm_priv, domain); 5262306a36Sopenharmony_ci} 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic int __enable_clocks(struct msm_iommu_dev *iommu) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci int ret; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci ret = clk_enable(iommu->pclk); 5962306a36Sopenharmony_ci if (ret) 6062306a36Sopenharmony_ci goto fail; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci if (iommu->clk) { 6362306a36Sopenharmony_ci ret = clk_enable(iommu->clk); 6462306a36Sopenharmony_ci if (ret) 6562306a36Sopenharmony_ci clk_disable(iommu->pclk); 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_cifail: 6862306a36Sopenharmony_ci return ret; 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic void __disable_clocks(struct msm_iommu_dev *iommu) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci if (iommu->clk) 7462306a36Sopenharmony_ci clk_disable(iommu->clk); 7562306a36Sopenharmony_ci clk_disable(iommu->pclk); 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic void msm_iommu_reset(void __iomem *base, int ncb) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci int ctx; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci SET_RPUE(base, 0); 8362306a36Sopenharmony_ci SET_RPUEIE(base, 0); 8462306a36Sopenharmony_ci SET_ESRRESTORE(base, 0); 8562306a36Sopenharmony_ci SET_TBE(base, 0); 8662306a36Sopenharmony_ci SET_CR(base, 0); 8762306a36Sopenharmony_ci SET_SPDMBE(base, 0); 8862306a36Sopenharmony_ci SET_TESTBUSCR(base, 0); 8962306a36Sopenharmony_ci SET_TLBRSW(base, 0); 9062306a36Sopenharmony_ci SET_GLOBAL_TLBIALL(base, 0); 9162306a36Sopenharmony_ci SET_RPU_ACR(base, 0); 9262306a36Sopenharmony_ci SET_TLBLKCRWE(base, 1); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci for (ctx = 0; ctx < ncb; ctx++) { 9562306a36Sopenharmony_ci SET_BPRCOSH(base, ctx, 0); 9662306a36Sopenharmony_ci SET_BPRCISH(base, ctx, 0); 9762306a36Sopenharmony_ci SET_BPRCNSH(base, ctx, 0); 9862306a36Sopenharmony_ci SET_BPSHCFG(base, ctx, 0); 9962306a36Sopenharmony_ci SET_BPMTCFG(base, ctx, 0); 10062306a36Sopenharmony_ci SET_ACTLR(base, ctx, 0); 10162306a36Sopenharmony_ci SET_SCTLR(base, ctx, 0); 10262306a36Sopenharmony_ci SET_FSRRESTORE(base, ctx, 0); 10362306a36Sopenharmony_ci SET_TTBR0(base, ctx, 0); 10462306a36Sopenharmony_ci SET_TTBR1(base, ctx, 0); 10562306a36Sopenharmony_ci SET_TTBCR(base, ctx, 0); 10662306a36Sopenharmony_ci SET_BFBCR(base, ctx, 0); 10762306a36Sopenharmony_ci SET_PAR(base, ctx, 0); 10862306a36Sopenharmony_ci SET_FAR(base, ctx, 0); 10962306a36Sopenharmony_ci SET_CTX_TLBIALL(base, ctx, 0); 11062306a36Sopenharmony_ci SET_TLBFLPTER(base, ctx, 0); 11162306a36Sopenharmony_ci SET_TLBSLPTER(base, ctx, 0); 11262306a36Sopenharmony_ci SET_TLBLKCR(base, ctx, 0); 11362306a36Sopenharmony_ci SET_CONTEXTIDR(base, ctx, 0); 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic void __flush_iotlb(void *cookie) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci struct msm_priv *priv = cookie; 12062306a36Sopenharmony_ci struct msm_iommu_dev *iommu = NULL; 12162306a36Sopenharmony_ci struct msm_iommu_ctx_dev *master; 12262306a36Sopenharmony_ci int ret = 0; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci list_for_each_entry(iommu, &priv->list_attached, dom_node) { 12562306a36Sopenharmony_ci ret = __enable_clocks(iommu); 12662306a36Sopenharmony_ci if (ret) 12762306a36Sopenharmony_ci goto fail; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci list_for_each_entry(master, &iommu->ctx_list, list) 13062306a36Sopenharmony_ci SET_CTX_TLBIALL(iommu->base, master->num, 0); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci __disable_clocks(iommu); 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_cifail: 13562306a36Sopenharmony_ci return; 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic void __flush_iotlb_range(unsigned long iova, size_t size, 13962306a36Sopenharmony_ci size_t granule, bool leaf, void *cookie) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci struct msm_priv *priv = cookie; 14262306a36Sopenharmony_ci struct msm_iommu_dev *iommu = NULL; 14362306a36Sopenharmony_ci struct msm_iommu_ctx_dev *master; 14462306a36Sopenharmony_ci int ret = 0; 14562306a36Sopenharmony_ci int temp_size; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci list_for_each_entry(iommu, &priv->list_attached, dom_node) { 14862306a36Sopenharmony_ci ret = __enable_clocks(iommu); 14962306a36Sopenharmony_ci if (ret) 15062306a36Sopenharmony_ci goto fail; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci list_for_each_entry(master, &iommu->ctx_list, list) { 15362306a36Sopenharmony_ci temp_size = size; 15462306a36Sopenharmony_ci do { 15562306a36Sopenharmony_ci iova &= TLBIVA_VA; 15662306a36Sopenharmony_ci iova |= GET_CONTEXTIDR_ASID(iommu->base, 15762306a36Sopenharmony_ci master->num); 15862306a36Sopenharmony_ci SET_TLBIVA(iommu->base, master->num, iova); 15962306a36Sopenharmony_ci iova += granule; 16062306a36Sopenharmony_ci } while (temp_size -= granule); 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci __disable_clocks(iommu); 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cifail: 16762306a36Sopenharmony_ci return; 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic void __flush_iotlb_walk(unsigned long iova, size_t size, 17162306a36Sopenharmony_ci size_t granule, void *cookie) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci __flush_iotlb_range(iova, size, granule, false, cookie); 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic void __flush_iotlb_page(struct iommu_iotlb_gather *gather, 17762306a36Sopenharmony_ci unsigned long iova, size_t granule, void *cookie) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci __flush_iotlb_range(iova, granule, granule, true, cookie); 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic const struct iommu_flush_ops msm_iommu_flush_ops = { 18362306a36Sopenharmony_ci .tlb_flush_all = __flush_iotlb, 18462306a36Sopenharmony_ci .tlb_flush_walk = __flush_iotlb_walk, 18562306a36Sopenharmony_ci .tlb_add_page = __flush_iotlb_page, 18662306a36Sopenharmony_ci}; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic int msm_iommu_alloc_ctx(unsigned long *map, int start, int end) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci int idx; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci do { 19362306a36Sopenharmony_ci idx = find_next_zero_bit(map, end, start); 19462306a36Sopenharmony_ci if (idx == end) 19562306a36Sopenharmony_ci return -ENOSPC; 19662306a36Sopenharmony_ci } while (test_and_set_bit(idx, map)); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci return idx; 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic void msm_iommu_free_ctx(unsigned long *map, int idx) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci clear_bit(idx, map); 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic void config_mids(struct msm_iommu_dev *iommu, 20762306a36Sopenharmony_ci struct msm_iommu_ctx_dev *master) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci int mid, ctx, i; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci for (i = 0; i < master->num_mids; i++) { 21262306a36Sopenharmony_ci mid = master->mids[i]; 21362306a36Sopenharmony_ci ctx = master->num; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci SET_M2VCBR_N(iommu->base, mid, 0); 21662306a36Sopenharmony_ci SET_CBACR_N(iommu->base, ctx, 0); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci /* Set VMID = 0 */ 21962306a36Sopenharmony_ci SET_VMID(iommu->base, mid, 0); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci /* Set the context number for that MID to this context */ 22262306a36Sopenharmony_ci SET_CBNDX(iommu->base, mid, ctx); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci /* Set MID associated with this context bank to 0*/ 22562306a36Sopenharmony_ci SET_CBVMID(iommu->base, ctx, 0); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci /* Set the ASID for TLB tagging for this context */ 22862306a36Sopenharmony_ci SET_CONTEXTIDR_ASID(iommu->base, ctx, ctx); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci /* Set security bit override to be Non-secure */ 23162306a36Sopenharmony_ci SET_NSCFG(iommu->base, mid, 3); 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic void __reset_context(void __iomem *base, int ctx) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci SET_BPRCOSH(base, ctx, 0); 23862306a36Sopenharmony_ci SET_BPRCISH(base, ctx, 0); 23962306a36Sopenharmony_ci SET_BPRCNSH(base, ctx, 0); 24062306a36Sopenharmony_ci SET_BPSHCFG(base, ctx, 0); 24162306a36Sopenharmony_ci SET_BPMTCFG(base, ctx, 0); 24262306a36Sopenharmony_ci SET_ACTLR(base, ctx, 0); 24362306a36Sopenharmony_ci SET_SCTLR(base, ctx, 0); 24462306a36Sopenharmony_ci SET_FSRRESTORE(base, ctx, 0); 24562306a36Sopenharmony_ci SET_TTBR0(base, ctx, 0); 24662306a36Sopenharmony_ci SET_TTBR1(base, ctx, 0); 24762306a36Sopenharmony_ci SET_TTBCR(base, ctx, 0); 24862306a36Sopenharmony_ci SET_BFBCR(base, ctx, 0); 24962306a36Sopenharmony_ci SET_PAR(base, ctx, 0); 25062306a36Sopenharmony_ci SET_FAR(base, ctx, 0); 25162306a36Sopenharmony_ci SET_CTX_TLBIALL(base, ctx, 0); 25262306a36Sopenharmony_ci SET_TLBFLPTER(base, ctx, 0); 25362306a36Sopenharmony_ci SET_TLBSLPTER(base, ctx, 0); 25462306a36Sopenharmony_ci SET_TLBLKCR(base, ctx, 0); 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic void __program_context(void __iomem *base, int ctx, 25862306a36Sopenharmony_ci struct msm_priv *priv) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci __reset_context(base, ctx); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci /* Turn on TEX Remap */ 26362306a36Sopenharmony_ci SET_TRE(base, ctx, 1); 26462306a36Sopenharmony_ci SET_AFE(base, ctx, 1); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci /* Set up HTW mode */ 26762306a36Sopenharmony_ci /* TLB miss configuration: perform HTW on miss */ 26862306a36Sopenharmony_ci SET_TLBMCFG(base, ctx, 0x3); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci /* V2P configuration: HTW for access */ 27162306a36Sopenharmony_ci SET_V2PCFG(base, ctx, 0x3); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci SET_TTBCR(base, ctx, priv->cfg.arm_v7s_cfg.tcr); 27462306a36Sopenharmony_ci SET_TTBR0(base, ctx, priv->cfg.arm_v7s_cfg.ttbr); 27562306a36Sopenharmony_ci SET_TTBR1(base, ctx, 0); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci /* Set prrr and nmrr */ 27862306a36Sopenharmony_ci SET_PRRR(base, ctx, priv->cfg.arm_v7s_cfg.prrr); 27962306a36Sopenharmony_ci SET_NMRR(base, ctx, priv->cfg.arm_v7s_cfg.nmrr); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci /* Invalidate the TLB for this context */ 28262306a36Sopenharmony_ci SET_CTX_TLBIALL(base, ctx, 0); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci /* Set interrupt number to "secure" interrupt */ 28562306a36Sopenharmony_ci SET_IRPTNDX(base, ctx, 0); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci /* Enable context fault interrupt */ 28862306a36Sopenharmony_ci SET_CFEIE(base, ctx, 1); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci /* Stall access on a context fault and let the handler deal with it */ 29162306a36Sopenharmony_ci SET_CFCFG(base, ctx, 1); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci /* Redirect all cacheable requests to L2 slave port. */ 29462306a36Sopenharmony_ci SET_RCISH(base, ctx, 1); 29562306a36Sopenharmony_ci SET_RCOSH(base, ctx, 1); 29662306a36Sopenharmony_ci SET_RCNSH(base, ctx, 1); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci /* Turn on BFB prefetch */ 29962306a36Sopenharmony_ci SET_BFBDFE(base, ctx, 1); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci /* Enable the MMU */ 30262306a36Sopenharmony_ci SET_M(base, ctx, 1); 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cistatic struct iommu_domain *msm_iommu_domain_alloc(unsigned type) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci struct msm_priv *priv; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci if (type != IOMMU_DOMAIN_UNMANAGED) 31062306a36Sopenharmony_ci return NULL; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci priv = kzalloc(sizeof(*priv), GFP_KERNEL); 31362306a36Sopenharmony_ci if (!priv) 31462306a36Sopenharmony_ci goto fail_nomem; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci INIT_LIST_HEAD(&priv->list_attached); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci priv->domain.geometry.aperture_start = 0; 31962306a36Sopenharmony_ci priv->domain.geometry.aperture_end = (1ULL << 32) - 1; 32062306a36Sopenharmony_ci priv->domain.geometry.force_aperture = true; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci return &priv->domain; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_cifail_nomem: 32562306a36Sopenharmony_ci kfree(priv); 32662306a36Sopenharmony_ci return NULL; 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_cistatic void msm_iommu_domain_free(struct iommu_domain *domain) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci struct msm_priv *priv; 33262306a36Sopenharmony_ci unsigned long flags; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci spin_lock_irqsave(&msm_iommu_lock, flags); 33562306a36Sopenharmony_ci priv = to_msm_priv(domain); 33662306a36Sopenharmony_ci kfree(priv); 33762306a36Sopenharmony_ci spin_unlock_irqrestore(&msm_iommu_lock, flags); 33862306a36Sopenharmony_ci} 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_cistatic int msm_iommu_domain_config(struct msm_priv *priv) 34162306a36Sopenharmony_ci{ 34262306a36Sopenharmony_ci spin_lock_init(&priv->pgtlock); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci priv->cfg = (struct io_pgtable_cfg) { 34562306a36Sopenharmony_ci .pgsize_bitmap = msm_iommu_ops.pgsize_bitmap, 34662306a36Sopenharmony_ci .ias = 32, 34762306a36Sopenharmony_ci .oas = 32, 34862306a36Sopenharmony_ci .tlb = &msm_iommu_flush_ops, 34962306a36Sopenharmony_ci .iommu_dev = priv->dev, 35062306a36Sopenharmony_ci }; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci priv->iop = alloc_io_pgtable_ops(ARM_V7S, &priv->cfg, priv); 35362306a36Sopenharmony_ci if (!priv->iop) { 35462306a36Sopenharmony_ci dev_err(priv->dev, "Failed to allocate pgtable\n"); 35562306a36Sopenharmony_ci return -EINVAL; 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci msm_iommu_ops.pgsize_bitmap = priv->cfg.pgsize_bitmap; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci return 0; 36162306a36Sopenharmony_ci} 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci/* Must be called under msm_iommu_lock */ 36462306a36Sopenharmony_cistatic struct msm_iommu_dev *find_iommu_for_dev(struct device *dev) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci struct msm_iommu_dev *iommu, *ret = NULL; 36762306a36Sopenharmony_ci struct msm_iommu_ctx_dev *master; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci list_for_each_entry(iommu, &qcom_iommu_devices, dev_node) { 37062306a36Sopenharmony_ci master = list_first_entry(&iommu->ctx_list, 37162306a36Sopenharmony_ci struct msm_iommu_ctx_dev, 37262306a36Sopenharmony_ci list); 37362306a36Sopenharmony_ci if (master->of_node == dev->of_node) { 37462306a36Sopenharmony_ci ret = iommu; 37562306a36Sopenharmony_ci break; 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci return ret; 38062306a36Sopenharmony_ci} 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_cistatic struct iommu_device *msm_iommu_probe_device(struct device *dev) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci struct msm_iommu_dev *iommu; 38562306a36Sopenharmony_ci unsigned long flags; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci spin_lock_irqsave(&msm_iommu_lock, flags); 38862306a36Sopenharmony_ci iommu = find_iommu_for_dev(dev); 38962306a36Sopenharmony_ci spin_unlock_irqrestore(&msm_iommu_lock, flags); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci if (!iommu) 39262306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci return &iommu->iommu; 39562306a36Sopenharmony_ci} 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_cistatic int msm_iommu_attach_dev(struct iommu_domain *domain, struct device *dev) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci int ret = 0; 40062306a36Sopenharmony_ci unsigned long flags; 40162306a36Sopenharmony_ci struct msm_iommu_dev *iommu; 40262306a36Sopenharmony_ci struct msm_priv *priv = to_msm_priv(domain); 40362306a36Sopenharmony_ci struct msm_iommu_ctx_dev *master; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci priv->dev = dev; 40662306a36Sopenharmony_ci msm_iommu_domain_config(priv); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci spin_lock_irqsave(&msm_iommu_lock, flags); 40962306a36Sopenharmony_ci list_for_each_entry(iommu, &qcom_iommu_devices, dev_node) { 41062306a36Sopenharmony_ci master = list_first_entry(&iommu->ctx_list, 41162306a36Sopenharmony_ci struct msm_iommu_ctx_dev, 41262306a36Sopenharmony_ci list); 41362306a36Sopenharmony_ci if (master->of_node == dev->of_node) { 41462306a36Sopenharmony_ci ret = __enable_clocks(iommu); 41562306a36Sopenharmony_ci if (ret) 41662306a36Sopenharmony_ci goto fail; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci list_for_each_entry(master, &iommu->ctx_list, list) { 41962306a36Sopenharmony_ci if (master->num) { 42062306a36Sopenharmony_ci dev_err(dev, "domain already attached"); 42162306a36Sopenharmony_ci ret = -EEXIST; 42262306a36Sopenharmony_ci goto fail; 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci master->num = 42562306a36Sopenharmony_ci msm_iommu_alloc_ctx(iommu->context_map, 42662306a36Sopenharmony_ci 0, iommu->ncb); 42762306a36Sopenharmony_ci if (IS_ERR_VALUE(master->num)) { 42862306a36Sopenharmony_ci ret = -ENODEV; 42962306a36Sopenharmony_ci goto fail; 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci config_mids(iommu, master); 43262306a36Sopenharmony_ci __program_context(iommu->base, master->num, 43362306a36Sopenharmony_ci priv); 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci __disable_clocks(iommu); 43662306a36Sopenharmony_ci list_add(&iommu->dom_node, &priv->list_attached); 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_cifail: 44162306a36Sopenharmony_ci spin_unlock_irqrestore(&msm_iommu_lock, flags); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci return ret; 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_cistatic void msm_iommu_set_platform_dma(struct device *dev) 44762306a36Sopenharmony_ci{ 44862306a36Sopenharmony_ci struct iommu_domain *domain = iommu_get_domain_for_dev(dev); 44962306a36Sopenharmony_ci struct msm_priv *priv = to_msm_priv(domain); 45062306a36Sopenharmony_ci unsigned long flags; 45162306a36Sopenharmony_ci struct msm_iommu_dev *iommu; 45262306a36Sopenharmony_ci struct msm_iommu_ctx_dev *master; 45362306a36Sopenharmony_ci int ret; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci free_io_pgtable_ops(priv->iop); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci spin_lock_irqsave(&msm_iommu_lock, flags); 45862306a36Sopenharmony_ci list_for_each_entry(iommu, &priv->list_attached, dom_node) { 45962306a36Sopenharmony_ci ret = __enable_clocks(iommu); 46062306a36Sopenharmony_ci if (ret) 46162306a36Sopenharmony_ci goto fail; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci list_for_each_entry(master, &iommu->ctx_list, list) { 46462306a36Sopenharmony_ci msm_iommu_free_ctx(iommu->context_map, master->num); 46562306a36Sopenharmony_ci __reset_context(iommu->base, master->num); 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci __disable_clocks(iommu); 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_cifail: 47062306a36Sopenharmony_ci spin_unlock_irqrestore(&msm_iommu_lock, flags); 47162306a36Sopenharmony_ci} 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_cistatic int msm_iommu_map(struct iommu_domain *domain, unsigned long iova, 47462306a36Sopenharmony_ci phys_addr_t pa, size_t pgsize, size_t pgcount, 47562306a36Sopenharmony_ci int prot, gfp_t gfp, size_t *mapped) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci struct msm_priv *priv = to_msm_priv(domain); 47862306a36Sopenharmony_ci unsigned long flags; 47962306a36Sopenharmony_ci int ret; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci spin_lock_irqsave(&priv->pgtlock, flags); 48262306a36Sopenharmony_ci ret = priv->iop->map_pages(priv->iop, iova, pa, pgsize, pgcount, prot, 48362306a36Sopenharmony_ci GFP_ATOMIC, mapped); 48462306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->pgtlock, flags); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci return ret; 48762306a36Sopenharmony_ci} 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_cistatic void msm_iommu_sync_map(struct iommu_domain *domain, unsigned long iova, 49062306a36Sopenharmony_ci size_t size) 49162306a36Sopenharmony_ci{ 49262306a36Sopenharmony_ci struct msm_priv *priv = to_msm_priv(domain); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci __flush_iotlb_range(iova, size, SZ_4K, false, priv); 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_cistatic size_t msm_iommu_unmap(struct iommu_domain *domain, unsigned long iova, 49862306a36Sopenharmony_ci size_t pgsize, size_t pgcount, 49962306a36Sopenharmony_ci struct iommu_iotlb_gather *gather) 50062306a36Sopenharmony_ci{ 50162306a36Sopenharmony_ci struct msm_priv *priv = to_msm_priv(domain); 50262306a36Sopenharmony_ci unsigned long flags; 50362306a36Sopenharmony_ci size_t ret; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci spin_lock_irqsave(&priv->pgtlock, flags); 50662306a36Sopenharmony_ci ret = priv->iop->unmap_pages(priv->iop, iova, pgsize, pgcount, gather); 50762306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->pgtlock, flags); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci return ret; 51062306a36Sopenharmony_ci} 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_cistatic phys_addr_t msm_iommu_iova_to_phys(struct iommu_domain *domain, 51362306a36Sopenharmony_ci dma_addr_t va) 51462306a36Sopenharmony_ci{ 51562306a36Sopenharmony_ci struct msm_priv *priv; 51662306a36Sopenharmony_ci struct msm_iommu_dev *iommu; 51762306a36Sopenharmony_ci struct msm_iommu_ctx_dev *master; 51862306a36Sopenharmony_ci unsigned int par; 51962306a36Sopenharmony_ci unsigned long flags; 52062306a36Sopenharmony_ci phys_addr_t ret = 0; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci spin_lock_irqsave(&msm_iommu_lock, flags); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci priv = to_msm_priv(domain); 52562306a36Sopenharmony_ci iommu = list_first_entry(&priv->list_attached, 52662306a36Sopenharmony_ci struct msm_iommu_dev, dom_node); 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci if (list_empty(&iommu->ctx_list)) 52962306a36Sopenharmony_ci goto fail; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci master = list_first_entry(&iommu->ctx_list, 53262306a36Sopenharmony_ci struct msm_iommu_ctx_dev, list); 53362306a36Sopenharmony_ci if (!master) 53462306a36Sopenharmony_ci goto fail; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci ret = __enable_clocks(iommu); 53762306a36Sopenharmony_ci if (ret) 53862306a36Sopenharmony_ci goto fail; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci /* Invalidate context TLB */ 54162306a36Sopenharmony_ci SET_CTX_TLBIALL(iommu->base, master->num, 0); 54262306a36Sopenharmony_ci SET_V2PPR(iommu->base, master->num, va & V2Pxx_VA); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci par = GET_PAR(iommu->base, master->num); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci /* We are dealing with a supersection */ 54762306a36Sopenharmony_ci if (GET_NOFAULT_SS(iommu->base, master->num)) 54862306a36Sopenharmony_ci ret = (par & 0xFF000000) | (va & 0x00FFFFFF); 54962306a36Sopenharmony_ci else /* Upper 20 bits from PAR, lower 12 from VA */ 55062306a36Sopenharmony_ci ret = (par & 0xFFFFF000) | (va & 0x00000FFF); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci if (GET_FAULT(iommu->base, master->num)) 55362306a36Sopenharmony_ci ret = 0; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci __disable_clocks(iommu); 55662306a36Sopenharmony_cifail: 55762306a36Sopenharmony_ci spin_unlock_irqrestore(&msm_iommu_lock, flags); 55862306a36Sopenharmony_ci return ret; 55962306a36Sopenharmony_ci} 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_cistatic void print_ctx_regs(void __iomem *base, int ctx) 56262306a36Sopenharmony_ci{ 56362306a36Sopenharmony_ci unsigned int fsr = GET_FSR(base, ctx); 56462306a36Sopenharmony_ci pr_err("FAR = %08x PAR = %08x\n", 56562306a36Sopenharmony_ci GET_FAR(base, ctx), GET_PAR(base, ctx)); 56662306a36Sopenharmony_ci pr_err("FSR = %08x [%s%s%s%s%s%s%s%s%s%s]\n", fsr, 56762306a36Sopenharmony_ci (fsr & 0x02) ? "TF " : "", 56862306a36Sopenharmony_ci (fsr & 0x04) ? "AFF " : "", 56962306a36Sopenharmony_ci (fsr & 0x08) ? "APF " : "", 57062306a36Sopenharmony_ci (fsr & 0x10) ? "TLBMF " : "", 57162306a36Sopenharmony_ci (fsr & 0x20) ? "HTWDEEF " : "", 57262306a36Sopenharmony_ci (fsr & 0x40) ? "HTWSEEF " : "", 57362306a36Sopenharmony_ci (fsr & 0x80) ? "MHF " : "", 57462306a36Sopenharmony_ci (fsr & 0x10000) ? "SL " : "", 57562306a36Sopenharmony_ci (fsr & 0x40000000) ? "SS " : "", 57662306a36Sopenharmony_ci (fsr & 0x80000000) ? "MULTI " : ""); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci pr_err("FSYNR0 = %08x FSYNR1 = %08x\n", 57962306a36Sopenharmony_ci GET_FSYNR0(base, ctx), GET_FSYNR1(base, ctx)); 58062306a36Sopenharmony_ci pr_err("TTBR0 = %08x TTBR1 = %08x\n", 58162306a36Sopenharmony_ci GET_TTBR0(base, ctx), GET_TTBR1(base, ctx)); 58262306a36Sopenharmony_ci pr_err("SCTLR = %08x ACTLR = %08x\n", 58362306a36Sopenharmony_ci GET_SCTLR(base, ctx), GET_ACTLR(base, ctx)); 58462306a36Sopenharmony_ci} 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_cistatic int insert_iommu_master(struct device *dev, 58762306a36Sopenharmony_ci struct msm_iommu_dev **iommu, 58862306a36Sopenharmony_ci struct of_phandle_args *spec) 58962306a36Sopenharmony_ci{ 59062306a36Sopenharmony_ci struct msm_iommu_ctx_dev *master = dev_iommu_priv_get(dev); 59162306a36Sopenharmony_ci int sid; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci if (list_empty(&(*iommu)->ctx_list)) { 59462306a36Sopenharmony_ci master = kzalloc(sizeof(*master), GFP_ATOMIC); 59562306a36Sopenharmony_ci if (!master) { 59662306a36Sopenharmony_ci dev_err(dev, "Failed to allocate iommu_master\n"); 59762306a36Sopenharmony_ci return -ENOMEM; 59862306a36Sopenharmony_ci } 59962306a36Sopenharmony_ci master->of_node = dev->of_node; 60062306a36Sopenharmony_ci list_add(&master->list, &(*iommu)->ctx_list); 60162306a36Sopenharmony_ci dev_iommu_priv_set(dev, master); 60262306a36Sopenharmony_ci } 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci for (sid = 0; sid < master->num_mids; sid++) 60562306a36Sopenharmony_ci if (master->mids[sid] == spec->args[0]) { 60662306a36Sopenharmony_ci dev_warn(dev, "Stream ID 0x%x repeated; ignoring\n", 60762306a36Sopenharmony_ci sid); 60862306a36Sopenharmony_ci return 0; 60962306a36Sopenharmony_ci } 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci master->mids[master->num_mids++] = spec->args[0]; 61262306a36Sopenharmony_ci return 0; 61362306a36Sopenharmony_ci} 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_cistatic int qcom_iommu_of_xlate(struct device *dev, 61662306a36Sopenharmony_ci struct of_phandle_args *spec) 61762306a36Sopenharmony_ci{ 61862306a36Sopenharmony_ci struct msm_iommu_dev *iommu = NULL, *iter; 61962306a36Sopenharmony_ci unsigned long flags; 62062306a36Sopenharmony_ci int ret = 0; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci spin_lock_irqsave(&msm_iommu_lock, flags); 62362306a36Sopenharmony_ci list_for_each_entry(iter, &qcom_iommu_devices, dev_node) { 62462306a36Sopenharmony_ci if (iter->dev->of_node == spec->np) { 62562306a36Sopenharmony_ci iommu = iter; 62662306a36Sopenharmony_ci break; 62762306a36Sopenharmony_ci } 62862306a36Sopenharmony_ci } 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci if (!iommu) { 63162306a36Sopenharmony_ci ret = -ENODEV; 63262306a36Sopenharmony_ci goto fail; 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci ret = insert_iommu_master(dev, &iommu, spec); 63662306a36Sopenharmony_cifail: 63762306a36Sopenharmony_ci spin_unlock_irqrestore(&msm_iommu_lock, flags); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci return ret; 64062306a36Sopenharmony_ci} 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ciirqreturn_t msm_iommu_fault_handler(int irq, void *dev_id) 64362306a36Sopenharmony_ci{ 64462306a36Sopenharmony_ci struct msm_iommu_dev *iommu = dev_id; 64562306a36Sopenharmony_ci unsigned int fsr; 64662306a36Sopenharmony_ci int i, ret; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci spin_lock(&msm_iommu_lock); 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci if (!iommu) { 65162306a36Sopenharmony_ci pr_err("Invalid device ID in context interrupt handler\n"); 65262306a36Sopenharmony_ci goto fail; 65362306a36Sopenharmony_ci } 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci pr_err("Unexpected IOMMU page fault!\n"); 65662306a36Sopenharmony_ci pr_err("base = %08x\n", (unsigned int)iommu->base); 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci ret = __enable_clocks(iommu); 65962306a36Sopenharmony_ci if (ret) 66062306a36Sopenharmony_ci goto fail; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci for (i = 0; i < iommu->ncb; i++) { 66362306a36Sopenharmony_ci fsr = GET_FSR(iommu->base, i); 66462306a36Sopenharmony_ci if (fsr) { 66562306a36Sopenharmony_ci pr_err("Fault occurred in context %d.\n", i); 66662306a36Sopenharmony_ci pr_err("Interesting registers:\n"); 66762306a36Sopenharmony_ci print_ctx_regs(iommu->base, i); 66862306a36Sopenharmony_ci SET_FSR(iommu->base, i, 0x4000000F); 66962306a36Sopenharmony_ci } 67062306a36Sopenharmony_ci } 67162306a36Sopenharmony_ci __disable_clocks(iommu); 67262306a36Sopenharmony_cifail: 67362306a36Sopenharmony_ci spin_unlock(&msm_iommu_lock); 67462306a36Sopenharmony_ci return 0; 67562306a36Sopenharmony_ci} 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_cistatic struct iommu_ops msm_iommu_ops = { 67862306a36Sopenharmony_ci .domain_alloc = msm_iommu_domain_alloc, 67962306a36Sopenharmony_ci .probe_device = msm_iommu_probe_device, 68062306a36Sopenharmony_ci .device_group = generic_device_group, 68162306a36Sopenharmony_ci .set_platform_dma_ops = msm_iommu_set_platform_dma, 68262306a36Sopenharmony_ci .pgsize_bitmap = MSM_IOMMU_PGSIZES, 68362306a36Sopenharmony_ci .of_xlate = qcom_iommu_of_xlate, 68462306a36Sopenharmony_ci .default_domain_ops = &(const struct iommu_domain_ops) { 68562306a36Sopenharmony_ci .attach_dev = msm_iommu_attach_dev, 68662306a36Sopenharmony_ci .map_pages = msm_iommu_map, 68762306a36Sopenharmony_ci .unmap_pages = msm_iommu_unmap, 68862306a36Sopenharmony_ci /* 68962306a36Sopenharmony_ci * Nothing is needed here, the barrier to guarantee 69062306a36Sopenharmony_ci * completion of the tlb sync operation is implicitly 69162306a36Sopenharmony_ci * taken care when the iommu client does a writel before 69262306a36Sopenharmony_ci * kick starting the other master. 69362306a36Sopenharmony_ci */ 69462306a36Sopenharmony_ci .iotlb_sync = NULL, 69562306a36Sopenharmony_ci .iotlb_sync_map = msm_iommu_sync_map, 69662306a36Sopenharmony_ci .iova_to_phys = msm_iommu_iova_to_phys, 69762306a36Sopenharmony_ci .free = msm_iommu_domain_free, 69862306a36Sopenharmony_ci } 69962306a36Sopenharmony_ci}; 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_cistatic int msm_iommu_probe(struct platform_device *pdev) 70262306a36Sopenharmony_ci{ 70362306a36Sopenharmony_ci struct resource *r; 70462306a36Sopenharmony_ci resource_size_t ioaddr; 70562306a36Sopenharmony_ci struct msm_iommu_dev *iommu; 70662306a36Sopenharmony_ci int ret, par, val; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci iommu = devm_kzalloc(&pdev->dev, sizeof(*iommu), GFP_KERNEL); 70962306a36Sopenharmony_ci if (!iommu) 71062306a36Sopenharmony_ci return -ENODEV; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci iommu->dev = &pdev->dev; 71362306a36Sopenharmony_ci INIT_LIST_HEAD(&iommu->ctx_list); 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci iommu->pclk = devm_clk_get(iommu->dev, "smmu_pclk"); 71662306a36Sopenharmony_ci if (IS_ERR(iommu->pclk)) 71762306a36Sopenharmony_ci return dev_err_probe(iommu->dev, PTR_ERR(iommu->pclk), 71862306a36Sopenharmony_ci "could not get smmu_pclk\n"); 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci ret = clk_prepare(iommu->pclk); 72162306a36Sopenharmony_ci if (ret) 72262306a36Sopenharmony_ci return dev_err_probe(iommu->dev, ret, 72362306a36Sopenharmony_ci "could not prepare smmu_pclk\n"); 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci iommu->clk = devm_clk_get(iommu->dev, "iommu_clk"); 72662306a36Sopenharmony_ci if (IS_ERR(iommu->clk)) { 72762306a36Sopenharmony_ci clk_unprepare(iommu->pclk); 72862306a36Sopenharmony_ci return dev_err_probe(iommu->dev, PTR_ERR(iommu->clk), 72962306a36Sopenharmony_ci "could not get iommu_clk\n"); 73062306a36Sopenharmony_ci } 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci ret = clk_prepare(iommu->clk); 73362306a36Sopenharmony_ci if (ret) { 73462306a36Sopenharmony_ci clk_unprepare(iommu->pclk); 73562306a36Sopenharmony_ci return dev_err_probe(iommu->dev, ret, "could not prepare iommu_clk\n"); 73662306a36Sopenharmony_ci } 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 73962306a36Sopenharmony_ci iommu->base = devm_ioremap_resource(iommu->dev, r); 74062306a36Sopenharmony_ci if (IS_ERR(iommu->base)) { 74162306a36Sopenharmony_ci ret = dev_err_probe(iommu->dev, PTR_ERR(iommu->base), "could not get iommu base\n"); 74262306a36Sopenharmony_ci goto fail; 74362306a36Sopenharmony_ci } 74462306a36Sopenharmony_ci ioaddr = r->start; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci iommu->irq = platform_get_irq(pdev, 0); 74762306a36Sopenharmony_ci if (iommu->irq < 0) { 74862306a36Sopenharmony_ci ret = -ENODEV; 74962306a36Sopenharmony_ci goto fail; 75062306a36Sopenharmony_ci } 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci ret = of_property_read_u32(iommu->dev->of_node, "qcom,ncb", &val); 75362306a36Sopenharmony_ci if (ret) { 75462306a36Sopenharmony_ci dev_err(iommu->dev, "could not get ncb\n"); 75562306a36Sopenharmony_ci goto fail; 75662306a36Sopenharmony_ci } 75762306a36Sopenharmony_ci iommu->ncb = val; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci msm_iommu_reset(iommu->base, iommu->ncb); 76062306a36Sopenharmony_ci SET_M(iommu->base, 0, 1); 76162306a36Sopenharmony_ci SET_PAR(iommu->base, 0, 0); 76262306a36Sopenharmony_ci SET_V2PCFG(iommu->base, 0, 1); 76362306a36Sopenharmony_ci SET_V2PPR(iommu->base, 0, 0); 76462306a36Sopenharmony_ci par = GET_PAR(iommu->base, 0); 76562306a36Sopenharmony_ci SET_V2PCFG(iommu->base, 0, 0); 76662306a36Sopenharmony_ci SET_M(iommu->base, 0, 0); 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci if (!par) { 76962306a36Sopenharmony_ci pr_err("Invalid PAR value detected\n"); 77062306a36Sopenharmony_ci ret = -ENODEV; 77162306a36Sopenharmony_ci goto fail; 77262306a36Sopenharmony_ci } 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci ret = devm_request_threaded_irq(iommu->dev, iommu->irq, NULL, 77562306a36Sopenharmony_ci msm_iommu_fault_handler, 77662306a36Sopenharmony_ci IRQF_ONESHOT | IRQF_SHARED, 77762306a36Sopenharmony_ci "msm_iommu_secure_irpt_handler", 77862306a36Sopenharmony_ci iommu); 77962306a36Sopenharmony_ci if (ret) { 78062306a36Sopenharmony_ci pr_err("Request IRQ %d failed with ret=%d\n", iommu->irq, ret); 78162306a36Sopenharmony_ci goto fail; 78262306a36Sopenharmony_ci } 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci list_add(&iommu->dev_node, &qcom_iommu_devices); 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci ret = iommu_device_sysfs_add(&iommu->iommu, iommu->dev, NULL, 78762306a36Sopenharmony_ci "msm-smmu.%pa", &ioaddr); 78862306a36Sopenharmony_ci if (ret) { 78962306a36Sopenharmony_ci pr_err("Could not add msm-smmu at %pa to sysfs\n", &ioaddr); 79062306a36Sopenharmony_ci goto fail; 79162306a36Sopenharmony_ci } 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci ret = iommu_device_register(&iommu->iommu, &msm_iommu_ops, &pdev->dev); 79462306a36Sopenharmony_ci if (ret) { 79562306a36Sopenharmony_ci pr_err("Could not register msm-smmu at %pa\n", &ioaddr); 79662306a36Sopenharmony_ci goto fail; 79762306a36Sopenharmony_ci } 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci pr_info("device mapped at %p, irq %d with %d ctx banks\n", 80062306a36Sopenharmony_ci iommu->base, iommu->irq, iommu->ncb); 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci return ret; 80362306a36Sopenharmony_cifail: 80462306a36Sopenharmony_ci clk_unprepare(iommu->clk); 80562306a36Sopenharmony_ci clk_unprepare(iommu->pclk); 80662306a36Sopenharmony_ci return ret; 80762306a36Sopenharmony_ci} 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_cistatic const struct of_device_id msm_iommu_dt_match[] = { 81062306a36Sopenharmony_ci { .compatible = "qcom,apq8064-iommu" }, 81162306a36Sopenharmony_ci {} 81262306a36Sopenharmony_ci}; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_cistatic void msm_iommu_remove(struct platform_device *pdev) 81562306a36Sopenharmony_ci{ 81662306a36Sopenharmony_ci struct msm_iommu_dev *iommu = platform_get_drvdata(pdev); 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci clk_unprepare(iommu->clk); 81962306a36Sopenharmony_ci clk_unprepare(iommu->pclk); 82062306a36Sopenharmony_ci} 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_cistatic struct platform_driver msm_iommu_driver = { 82362306a36Sopenharmony_ci .driver = { 82462306a36Sopenharmony_ci .name = "msm_iommu", 82562306a36Sopenharmony_ci .of_match_table = msm_iommu_dt_match, 82662306a36Sopenharmony_ci }, 82762306a36Sopenharmony_ci .probe = msm_iommu_probe, 82862306a36Sopenharmony_ci .remove_new = msm_iommu_remove, 82962306a36Sopenharmony_ci}; 83062306a36Sopenharmony_cibuiltin_platform_driver(msm_iommu_driver); 831