162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * IOMMU API for ARM architected SMMUv3 implementations. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2015 ARM Limited 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Will Deacon <will.deacon@arm.com> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * This driver is powered by bad coffee and bombay mix. 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/acpi.h> 1362306a36Sopenharmony_ci#include <linux/acpi_iort.h> 1462306a36Sopenharmony_ci#include <linux/bitops.h> 1562306a36Sopenharmony_ci#include <linux/crash_dump.h> 1662306a36Sopenharmony_ci#include <linux/delay.h> 1762306a36Sopenharmony_ci#include <linux/err.h> 1862306a36Sopenharmony_ci#include <linux/interrupt.h> 1962306a36Sopenharmony_ci#include <linux/io-pgtable.h> 2062306a36Sopenharmony_ci#include <linux/iopoll.h> 2162306a36Sopenharmony_ci#include <linux/module.h> 2262306a36Sopenharmony_ci#include <linux/msi.h> 2362306a36Sopenharmony_ci#include <linux/of.h> 2462306a36Sopenharmony_ci#include <linux/of_address.h> 2562306a36Sopenharmony_ci#include <linux/of_platform.h> 2662306a36Sopenharmony_ci#include <linux/pci.h> 2762306a36Sopenharmony_ci#include <linux/pci-ats.h> 2862306a36Sopenharmony_ci#include <linux/platform_device.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#include "arm-smmu-v3.h" 3162306a36Sopenharmony_ci#include "../../dma-iommu.h" 3262306a36Sopenharmony_ci#include "../../iommu-sva.h" 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic bool disable_bypass = true; 3562306a36Sopenharmony_cimodule_param(disable_bypass, bool, 0444); 3662306a36Sopenharmony_ciMODULE_PARM_DESC(disable_bypass, 3762306a36Sopenharmony_ci "Disable bypass streams such that incoming transactions from devices that are not attached to an iommu domain will report an abort back to the device and will not be allowed to pass through the SMMU."); 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic bool disable_msipolling; 4062306a36Sopenharmony_cimodule_param(disable_msipolling, bool, 0444); 4162306a36Sopenharmony_ciMODULE_PARM_DESC(disable_msipolling, 4262306a36Sopenharmony_ci "Disable MSI-based polling for CMD_SYNC completion."); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cienum arm_smmu_msi_index { 4562306a36Sopenharmony_ci EVTQ_MSI_INDEX, 4662306a36Sopenharmony_ci GERROR_MSI_INDEX, 4762306a36Sopenharmony_ci PRIQ_MSI_INDEX, 4862306a36Sopenharmony_ci ARM_SMMU_MAX_MSIS, 4962306a36Sopenharmony_ci}; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic phys_addr_t arm_smmu_msi_cfg[ARM_SMMU_MAX_MSIS][3] = { 5262306a36Sopenharmony_ci [EVTQ_MSI_INDEX] = { 5362306a36Sopenharmony_ci ARM_SMMU_EVTQ_IRQ_CFG0, 5462306a36Sopenharmony_ci ARM_SMMU_EVTQ_IRQ_CFG1, 5562306a36Sopenharmony_ci ARM_SMMU_EVTQ_IRQ_CFG2, 5662306a36Sopenharmony_ci }, 5762306a36Sopenharmony_ci [GERROR_MSI_INDEX] = { 5862306a36Sopenharmony_ci ARM_SMMU_GERROR_IRQ_CFG0, 5962306a36Sopenharmony_ci ARM_SMMU_GERROR_IRQ_CFG1, 6062306a36Sopenharmony_ci ARM_SMMU_GERROR_IRQ_CFG2, 6162306a36Sopenharmony_ci }, 6262306a36Sopenharmony_ci [PRIQ_MSI_INDEX] = { 6362306a36Sopenharmony_ci ARM_SMMU_PRIQ_IRQ_CFG0, 6462306a36Sopenharmony_ci ARM_SMMU_PRIQ_IRQ_CFG1, 6562306a36Sopenharmony_ci ARM_SMMU_PRIQ_IRQ_CFG2, 6662306a36Sopenharmony_ci }, 6762306a36Sopenharmony_ci}; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistruct arm_smmu_option_prop { 7062306a36Sopenharmony_ci u32 opt; 7162306a36Sopenharmony_ci const char *prop; 7262306a36Sopenharmony_ci}; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ciDEFINE_XARRAY_ALLOC1(arm_smmu_asid_xa); 7562306a36Sopenharmony_ciDEFINE_MUTEX(arm_smmu_asid_lock); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci/* 7862306a36Sopenharmony_ci * Special value used by SVA when a process dies, to quiesce a CD without 7962306a36Sopenharmony_ci * disabling it. 8062306a36Sopenharmony_ci */ 8162306a36Sopenharmony_cistruct arm_smmu_ctx_desc quiet_cd = { 0 }; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic struct arm_smmu_option_prop arm_smmu_options[] = { 8462306a36Sopenharmony_ci { ARM_SMMU_OPT_SKIP_PREFETCH, "hisilicon,broken-prefetch-cmd" }, 8562306a36Sopenharmony_ci { ARM_SMMU_OPT_PAGE0_REGS_ONLY, "cavium,cn9900-broken-page1-regspace"}, 8662306a36Sopenharmony_ci { 0, NULL}, 8762306a36Sopenharmony_ci}; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic void parse_driver_options(struct arm_smmu_device *smmu) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci int i = 0; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci do { 9462306a36Sopenharmony_ci if (of_property_read_bool(smmu->dev->of_node, 9562306a36Sopenharmony_ci arm_smmu_options[i].prop)) { 9662306a36Sopenharmony_ci smmu->options |= arm_smmu_options[i].opt; 9762306a36Sopenharmony_ci dev_notice(smmu->dev, "option %s\n", 9862306a36Sopenharmony_ci arm_smmu_options[i].prop); 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci } while (arm_smmu_options[++i].opt); 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci/* Low-level queue manipulation functions */ 10462306a36Sopenharmony_cistatic bool queue_has_space(struct arm_smmu_ll_queue *q, u32 n) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci u32 space, prod, cons; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci prod = Q_IDX(q, q->prod); 10962306a36Sopenharmony_ci cons = Q_IDX(q, q->cons); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci if (Q_WRP(q, q->prod) == Q_WRP(q, q->cons)) 11262306a36Sopenharmony_ci space = (1 << q->max_n_shift) - (prod - cons); 11362306a36Sopenharmony_ci else 11462306a36Sopenharmony_ci space = cons - prod; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci return space >= n; 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic bool queue_full(struct arm_smmu_ll_queue *q) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci return Q_IDX(q, q->prod) == Q_IDX(q, q->cons) && 12262306a36Sopenharmony_ci Q_WRP(q, q->prod) != Q_WRP(q, q->cons); 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic bool queue_empty(struct arm_smmu_ll_queue *q) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci return Q_IDX(q, q->prod) == Q_IDX(q, q->cons) && 12862306a36Sopenharmony_ci Q_WRP(q, q->prod) == Q_WRP(q, q->cons); 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic bool queue_consumed(struct arm_smmu_ll_queue *q, u32 prod) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci return ((Q_WRP(q, q->cons) == Q_WRP(q, prod)) && 13462306a36Sopenharmony_ci (Q_IDX(q, q->cons) > Q_IDX(q, prod))) || 13562306a36Sopenharmony_ci ((Q_WRP(q, q->cons) != Q_WRP(q, prod)) && 13662306a36Sopenharmony_ci (Q_IDX(q, q->cons) <= Q_IDX(q, prod))); 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistatic void queue_sync_cons_out(struct arm_smmu_queue *q) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci /* 14262306a36Sopenharmony_ci * Ensure that all CPU accesses (reads and writes) to the queue 14362306a36Sopenharmony_ci * are complete before we update the cons pointer. 14462306a36Sopenharmony_ci */ 14562306a36Sopenharmony_ci __iomb(); 14662306a36Sopenharmony_ci writel_relaxed(q->llq.cons, q->cons_reg); 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic void queue_inc_cons(struct arm_smmu_ll_queue *q) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci u32 cons = (Q_WRP(q, q->cons) | Q_IDX(q, q->cons)) + 1; 15262306a36Sopenharmony_ci q->cons = Q_OVF(q->cons) | Q_WRP(q, cons) | Q_IDX(q, cons); 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic void queue_sync_cons_ovf(struct arm_smmu_queue *q) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci struct arm_smmu_ll_queue *llq = &q->llq; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci if (likely(Q_OVF(llq->prod) == Q_OVF(llq->cons))) 16062306a36Sopenharmony_ci return; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci llq->cons = Q_OVF(llq->prod) | Q_WRP(llq, llq->cons) | 16362306a36Sopenharmony_ci Q_IDX(llq, llq->cons); 16462306a36Sopenharmony_ci queue_sync_cons_out(q); 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic int queue_sync_prod_in(struct arm_smmu_queue *q) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci u32 prod; 17062306a36Sopenharmony_ci int ret = 0; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci /* 17362306a36Sopenharmony_ci * We can't use the _relaxed() variant here, as we must prevent 17462306a36Sopenharmony_ci * speculative reads of the queue before we have determined that 17562306a36Sopenharmony_ci * prod has indeed moved. 17662306a36Sopenharmony_ci */ 17762306a36Sopenharmony_ci prod = readl(q->prod_reg); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci if (Q_OVF(prod) != Q_OVF(q->llq.prod)) 18062306a36Sopenharmony_ci ret = -EOVERFLOW; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci q->llq.prod = prod; 18362306a36Sopenharmony_ci return ret; 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cistatic u32 queue_inc_prod_n(struct arm_smmu_ll_queue *q, int n) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci u32 prod = (Q_WRP(q, q->prod) | Q_IDX(q, q->prod)) + n; 18962306a36Sopenharmony_ci return Q_OVF(q->prod) | Q_WRP(q, prod) | Q_IDX(q, prod); 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic void queue_poll_init(struct arm_smmu_device *smmu, 19362306a36Sopenharmony_ci struct arm_smmu_queue_poll *qp) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci qp->delay = 1; 19662306a36Sopenharmony_ci qp->spin_cnt = 0; 19762306a36Sopenharmony_ci qp->wfe = !!(smmu->features & ARM_SMMU_FEAT_SEV); 19862306a36Sopenharmony_ci qp->timeout = ktime_add_us(ktime_get(), ARM_SMMU_POLL_TIMEOUT_US); 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic int queue_poll(struct arm_smmu_queue_poll *qp) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci if (ktime_compare(ktime_get(), qp->timeout) > 0) 20462306a36Sopenharmony_ci return -ETIMEDOUT; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci if (qp->wfe) { 20762306a36Sopenharmony_ci wfe(); 20862306a36Sopenharmony_ci } else if (++qp->spin_cnt < ARM_SMMU_POLL_SPIN_COUNT) { 20962306a36Sopenharmony_ci cpu_relax(); 21062306a36Sopenharmony_ci } else { 21162306a36Sopenharmony_ci udelay(qp->delay); 21262306a36Sopenharmony_ci qp->delay *= 2; 21362306a36Sopenharmony_ci qp->spin_cnt = 0; 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci return 0; 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic void queue_write(__le64 *dst, u64 *src, size_t n_dwords) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci int i; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci for (i = 0; i < n_dwords; ++i) 22462306a36Sopenharmony_ci *dst++ = cpu_to_le64(*src++); 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic void queue_read(u64 *dst, __le64 *src, size_t n_dwords) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci int i; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci for (i = 0; i < n_dwords; ++i) 23262306a36Sopenharmony_ci *dst++ = le64_to_cpu(*src++); 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic int queue_remove_raw(struct arm_smmu_queue *q, u64 *ent) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci if (queue_empty(&q->llq)) 23862306a36Sopenharmony_ci return -EAGAIN; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci queue_read(ent, Q_ENT(q, q->llq.cons), q->ent_dwords); 24162306a36Sopenharmony_ci queue_inc_cons(&q->llq); 24262306a36Sopenharmony_ci queue_sync_cons_out(q); 24362306a36Sopenharmony_ci return 0; 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci/* High-level queue accessors */ 24762306a36Sopenharmony_cistatic int arm_smmu_cmdq_build_cmd(u64 *cmd, struct arm_smmu_cmdq_ent *ent) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci memset(cmd, 0, 1 << CMDQ_ENT_SZ_SHIFT); 25062306a36Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_0_OP, ent->opcode); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci switch (ent->opcode) { 25362306a36Sopenharmony_ci case CMDQ_OP_TLBI_EL2_ALL: 25462306a36Sopenharmony_ci case CMDQ_OP_TLBI_NSNH_ALL: 25562306a36Sopenharmony_ci break; 25662306a36Sopenharmony_ci case CMDQ_OP_PREFETCH_CFG: 25762306a36Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_PREFETCH_0_SID, ent->prefetch.sid); 25862306a36Sopenharmony_ci break; 25962306a36Sopenharmony_ci case CMDQ_OP_CFGI_CD: 26062306a36Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_CFGI_0_SSID, ent->cfgi.ssid); 26162306a36Sopenharmony_ci fallthrough; 26262306a36Sopenharmony_ci case CMDQ_OP_CFGI_STE: 26362306a36Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_CFGI_0_SID, ent->cfgi.sid); 26462306a36Sopenharmony_ci cmd[1] |= FIELD_PREP(CMDQ_CFGI_1_LEAF, ent->cfgi.leaf); 26562306a36Sopenharmony_ci break; 26662306a36Sopenharmony_ci case CMDQ_OP_CFGI_CD_ALL: 26762306a36Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_CFGI_0_SID, ent->cfgi.sid); 26862306a36Sopenharmony_ci break; 26962306a36Sopenharmony_ci case CMDQ_OP_CFGI_ALL: 27062306a36Sopenharmony_ci /* Cover the entire SID range */ 27162306a36Sopenharmony_ci cmd[1] |= FIELD_PREP(CMDQ_CFGI_1_RANGE, 31); 27262306a36Sopenharmony_ci break; 27362306a36Sopenharmony_ci case CMDQ_OP_TLBI_NH_VA: 27462306a36Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_VMID, ent->tlbi.vmid); 27562306a36Sopenharmony_ci fallthrough; 27662306a36Sopenharmony_ci case CMDQ_OP_TLBI_EL2_VA: 27762306a36Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_NUM, ent->tlbi.num); 27862306a36Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_SCALE, ent->tlbi.scale); 27962306a36Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_ASID, ent->tlbi.asid); 28062306a36Sopenharmony_ci cmd[1] |= FIELD_PREP(CMDQ_TLBI_1_LEAF, ent->tlbi.leaf); 28162306a36Sopenharmony_ci cmd[1] |= FIELD_PREP(CMDQ_TLBI_1_TTL, ent->tlbi.ttl); 28262306a36Sopenharmony_ci cmd[1] |= FIELD_PREP(CMDQ_TLBI_1_TG, ent->tlbi.tg); 28362306a36Sopenharmony_ci cmd[1] |= ent->tlbi.addr & CMDQ_TLBI_1_VA_MASK; 28462306a36Sopenharmony_ci break; 28562306a36Sopenharmony_ci case CMDQ_OP_TLBI_S2_IPA: 28662306a36Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_NUM, ent->tlbi.num); 28762306a36Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_SCALE, ent->tlbi.scale); 28862306a36Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_VMID, ent->tlbi.vmid); 28962306a36Sopenharmony_ci cmd[1] |= FIELD_PREP(CMDQ_TLBI_1_LEAF, ent->tlbi.leaf); 29062306a36Sopenharmony_ci cmd[1] |= FIELD_PREP(CMDQ_TLBI_1_TTL, ent->tlbi.ttl); 29162306a36Sopenharmony_ci cmd[1] |= FIELD_PREP(CMDQ_TLBI_1_TG, ent->tlbi.tg); 29262306a36Sopenharmony_ci cmd[1] |= ent->tlbi.addr & CMDQ_TLBI_1_IPA_MASK; 29362306a36Sopenharmony_ci break; 29462306a36Sopenharmony_ci case CMDQ_OP_TLBI_NH_ASID: 29562306a36Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_ASID, ent->tlbi.asid); 29662306a36Sopenharmony_ci fallthrough; 29762306a36Sopenharmony_ci case CMDQ_OP_TLBI_S12_VMALL: 29862306a36Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_VMID, ent->tlbi.vmid); 29962306a36Sopenharmony_ci break; 30062306a36Sopenharmony_ci case CMDQ_OP_TLBI_EL2_ASID: 30162306a36Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_ASID, ent->tlbi.asid); 30262306a36Sopenharmony_ci break; 30362306a36Sopenharmony_ci case CMDQ_OP_ATC_INV: 30462306a36Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_0_SSV, ent->substream_valid); 30562306a36Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_ATC_0_GLOBAL, ent->atc.global); 30662306a36Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_ATC_0_SSID, ent->atc.ssid); 30762306a36Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_ATC_0_SID, ent->atc.sid); 30862306a36Sopenharmony_ci cmd[1] |= FIELD_PREP(CMDQ_ATC_1_SIZE, ent->atc.size); 30962306a36Sopenharmony_ci cmd[1] |= ent->atc.addr & CMDQ_ATC_1_ADDR_MASK; 31062306a36Sopenharmony_ci break; 31162306a36Sopenharmony_ci case CMDQ_OP_PRI_RESP: 31262306a36Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_0_SSV, ent->substream_valid); 31362306a36Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_PRI_0_SSID, ent->pri.ssid); 31462306a36Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_PRI_0_SID, ent->pri.sid); 31562306a36Sopenharmony_ci cmd[1] |= FIELD_PREP(CMDQ_PRI_1_GRPID, ent->pri.grpid); 31662306a36Sopenharmony_ci switch (ent->pri.resp) { 31762306a36Sopenharmony_ci case PRI_RESP_DENY: 31862306a36Sopenharmony_ci case PRI_RESP_FAIL: 31962306a36Sopenharmony_ci case PRI_RESP_SUCC: 32062306a36Sopenharmony_ci break; 32162306a36Sopenharmony_ci default: 32262306a36Sopenharmony_ci return -EINVAL; 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci cmd[1] |= FIELD_PREP(CMDQ_PRI_1_RESP, ent->pri.resp); 32562306a36Sopenharmony_ci break; 32662306a36Sopenharmony_ci case CMDQ_OP_RESUME: 32762306a36Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_RESUME_0_SID, ent->resume.sid); 32862306a36Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_RESUME_0_RESP, ent->resume.resp); 32962306a36Sopenharmony_ci cmd[1] |= FIELD_PREP(CMDQ_RESUME_1_STAG, ent->resume.stag); 33062306a36Sopenharmony_ci break; 33162306a36Sopenharmony_ci case CMDQ_OP_CMD_SYNC: 33262306a36Sopenharmony_ci if (ent->sync.msiaddr) { 33362306a36Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_SYNC_0_CS, CMDQ_SYNC_0_CS_IRQ); 33462306a36Sopenharmony_ci cmd[1] |= ent->sync.msiaddr & CMDQ_SYNC_1_MSIADDR_MASK; 33562306a36Sopenharmony_ci } else { 33662306a36Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_SYNC_0_CS, CMDQ_SYNC_0_CS_SEV); 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_SYNC_0_MSH, ARM_SMMU_SH_ISH); 33962306a36Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_SYNC_0_MSIATTR, ARM_SMMU_MEMATTR_OIWB); 34062306a36Sopenharmony_ci break; 34162306a36Sopenharmony_ci default: 34262306a36Sopenharmony_ci return -ENOENT; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci return 0; 34662306a36Sopenharmony_ci} 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_cistatic struct arm_smmu_cmdq *arm_smmu_get_cmdq(struct arm_smmu_device *smmu) 34962306a36Sopenharmony_ci{ 35062306a36Sopenharmony_ci return &smmu->cmdq; 35162306a36Sopenharmony_ci} 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_cistatic void arm_smmu_cmdq_build_sync_cmd(u64 *cmd, struct arm_smmu_device *smmu, 35462306a36Sopenharmony_ci struct arm_smmu_queue *q, u32 prod) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci struct arm_smmu_cmdq_ent ent = { 35762306a36Sopenharmony_ci .opcode = CMDQ_OP_CMD_SYNC, 35862306a36Sopenharmony_ci }; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci /* 36162306a36Sopenharmony_ci * Beware that Hi16xx adds an extra 32 bits of goodness to its MSI 36262306a36Sopenharmony_ci * payload, so the write will zero the entire command on that platform. 36362306a36Sopenharmony_ci */ 36462306a36Sopenharmony_ci if (smmu->options & ARM_SMMU_OPT_MSIPOLL) { 36562306a36Sopenharmony_ci ent.sync.msiaddr = q->base_dma + Q_IDX(&q->llq, prod) * 36662306a36Sopenharmony_ci q->ent_dwords * 8; 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci arm_smmu_cmdq_build_cmd(cmd, &ent); 37062306a36Sopenharmony_ci} 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_cistatic void __arm_smmu_cmdq_skip_err(struct arm_smmu_device *smmu, 37362306a36Sopenharmony_ci struct arm_smmu_queue *q) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci static const char * const cerror_str[] = { 37662306a36Sopenharmony_ci [CMDQ_ERR_CERROR_NONE_IDX] = "No error", 37762306a36Sopenharmony_ci [CMDQ_ERR_CERROR_ILL_IDX] = "Illegal command", 37862306a36Sopenharmony_ci [CMDQ_ERR_CERROR_ABT_IDX] = "Abort on command fetch", 37962306a36Sopenharmony_ci [CMDQ_ERR_CERROR_ATC_INV_IDX] = "ATC invalidate timeout", 38062306a36Sopenharmony_ci }; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci int i; 38362306a36Sopenharmony_ci u64 cmd[CMDQ_ENT_DWORDS]; 38462306a36Sopenharmony_ci u32 cons = readl_relaxed(q->cons_reg); 38562306a36Sopenharmony_ci u32 idx = FIELD_GET(CMDQ_CONS_ERR, cons); 38662306a36Sopenharmony_ci struct arm_smmu_cmdq_ent cmd_sync = { 38762306a36Sopenharmony_ci .opcode = CMDQ_OP_CMD_SYNC, 38862306a36Sopenharmony_ci }; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci dev_err(smmu->dev, "CMDQ error (cons 0x%08x): %s\n", cons, 39162306a36Sopenharmony_ci idx < ARRAY_SIZE(cerror_str) ? cerror_str[idx] : "Unknown"); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci switch (idx) { 39462306a36Sopenharmony_ci case CMDQ_ERR_CERROR_ABT_IDX: 39562306a36Sopenharmony_ci dev_err(smmu->dev, "retrying command fetch\n"); 39662306a36Sopenharmony_ci return; 39762306a36Sopenharmony_ci case CMDQ_ERR_CERROR_NONE_IDX: 39862306a36Sopenharmony_ci return; 39962306a36Sopenharmony_ci case CMDQ_ERR_CERROR_ATC_INV_IDX: 40062306a36Sopenharmony_ci /* 40162306a36Sopenharmony_ci * ATC Invalidation Completion timeout. CONS is still pointing 40262306a36Sopenharmony_ci * at the CMD_SYNC. Attempt to complete other pending commands 40362306a36Sopenharmony_ci * by repeating the CMD_SYNC, though we might well end up back 40462306a36Sopenharmony_ci * here since the ATC invalidation may still be pending. 40562306a36Sopenharmony_ci */ 40662306a36Sopenharmony_ci return; 40762306a36Sopenharmony_ci case CMDQ_ERR_CERROR_ILL_IDX: 40862306a36Sopenharmony_ci default: 40962306a36Sopenharmony_ci break; 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci /* 41362306a36Sopenharmony_ci * We may have concurrent producers, so we need to be careful 41462306a36Sopenharmony_ci * not to touch any of the shadow cmdq state. 41562306a36Sopenharmony_ci */ 41662306a36Sopenharmony_ci queue_read(cmd, Q_ENT(q, cons), q->ent_dwords); 41762306a36Sopenharmony_ci dev_err(smmu->dev, "skipping command in error state:\n"); 41862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(cmd); ++i) 41962306a36Sopenharmony_ci dev_err(smmu->dev, "\t0x%016llx\n", (unsigned long long)cmd[i]); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci /* Convert the erroneous command into a CMD_SYNC */ 42262306a36Sopenharmony_ci arm_smmu_cmdq_build_cmd(cmd, &cmd_sync); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci queue_write(Q_ENT(q, cons), cmd, q->ent_dwords); 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_cistatic void arm_smmu_cmdq_skip_err(struct arm_smmu_device *smmu) 42862306a36Sopenharmony_ci{ 42962306a36Sopenharmony_ci __arm_smmu_cmdq_skip_err(smmu, &smmu->cmdq.q); 43062306a36Sopenharmony_ci} 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci/* 43362306a36Sopenharmony_ci * Command queue locking. 43462306a36Sopenharmony_ci * This is a form of bastardised rwlock with the following major changes: 43562306a36Sopenharmony_ci * 43662306a36Sopenharmony_ci * - The only LOCK routines are exclusive_trylock() and shared_lock(). 43762306a36Sopenharmony_ci * Neither have barrier semantics, and instead provide only a control 43862306a36Sopenharmony_ci * dependency. 43962306a36Sopenharmony_ci * 44062306a36Sopenharmony_ci * - The UNLOCK routines are supplemented with shared_tryunlock(), which 44162306a36Sopenharmony_ci * fails if the caller appears to be the last lock holder (yes, this is 44262306a36Sopenharmony_ci * racy). All successful UNLOCK routines have RELEASE semantics. 44362306a36Sopenharmony_ci */ 44462306a36Sopenharmony_cistatic void arm_smmu_cmdq_shared_lock(struct arm_smmu_cmdq *cmdq) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci int val; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci /* 44962306a36Sopenharmony_ci * We can try to avoid the cmpxchg() loop by simply incrementing the 45062306a36Sopenharmony_ci * lock counter. When held in exclusive state, the lock counter is set 45162306a36Sopenharmony_ci * to INT_MIN so these increments won't hurt as the value will remain 45262306a36Sopenharmony_ci * negative. 45362306a36Sopenharmony_ci */ 45462306a36Sopenharmony_ci if (atomic_fetch_inc_relaxed(&cmdq->lock) >= 0) 45562306a36Sopenharmony_ci return; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci do { 45862306a36Sopenharmony_ci val = atomic_cond_read_relaxed(&cmdq->lock, VAL >= 0); 45962306a36Sopenharmony_ci } while (atomic_cmpxchg_relaxed(&cmdq->lock, val, val + 1) != val); 46062306a36Sopenharmony_ci} 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_cistatic void arm_smmu_cmdq_shared_unlock(struct arm_smmu_cmdq *cmdq) 46362306a36Sopenharmony_ci{ 46462306a36Sopenharmony_ci (void)atomic_dec_return_release(&cmdq->lock); 46562306a36Sopenharmony_ci} 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_cistatic bool arm_smmu_cmdq_shared_tryunlock(struct arm_smmu_cmdq *cmdq) 46862306a36Sopenharmony_ci{ 46962306a36Sopenharmony_ci if (atomic_read(&cmdq->lock) == 1) 47062306a36Sopenharmony_ci return false; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci arm_smmu_cmdq_shared_unlock(cmdq); 47362306a36Sopenharmony_ci return true; 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci#define arm_smmu_cmdq_exclusive_trylock_irqsave(cmdq, flags) \ 47762306a36Sopenharmony_ci({ \ 47862306a36Sopenharmony_ci bool __ret; \ 47962306a36Sopenharmony_ci local_irq_save(flags); \ 48062306a36Sopenharmony_ci __ret = !atomic_cmpxchg_relaxed(&cmdq->lock, 0, INT_MIN); \ 48162306a36Sopenharmony_ci if (!__ret) \ 48262306a36Sopenharmony_ci local_irq_restore(flags); \ 48362306a36Sopenharmony_ci __ret; \ 48462306a36Sopenharmony_ci}) 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci#define arm_smmu_cmdq_exclusive_unlock_irqrestore(cmdq, flags) \ 48762306a36Sopenharmony_ci({ \ 48862306a36Sopenharmony_ci atomic_set_release(&cmdq->lock, 0); \ 48962306a36Sopenharmony_ci local_irq_restore(flags); \ 49062306a36Sopenharmony_ci}) 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci/* 49462306a36Sopenharmony_ci * Command queue insertion. 49562306a36Sopenharmony_ci * This is made fiddly by our attempts to achieve some sort of scalability 49662306a36Sopenharmony_ci * since there is one queue shared amongst all of the CPUs in the system. If 49762306a36Sopenharmony_ci * you like mixed-size concurrency, dependency ordering and relaxed atomics, 49862306a36Sopenharmony_ci * then you'll *love* this monstrosity. 49962306a36Sopenharmony_ci * 50062306a36Sopenharmony_ci * The basic idea is to split the queue up into ranges of commands that are 50162306a36Sopenharmony_ci * owned by a given CPU; the owner may not have written all of the commands 50262306a36Sopenharmony_ci * itself, but is responsible for advancing the hardware prod pointer when 50362306a36Sopenharmony_ci * the time comes. The algorithm is roughly: 50462306a36Sopenharmony_ci * 50562306a36Sopenharmony_ci * 1. Allocate some space in the queue. At this point we also discover 50662306a36Sopenharmony_ci * whether the head of the queue is currently owned by another CPU, 50762306a36Sopenharmony_ci * or whether we are the owner. 50862306a36Sopenharmony_ci * 50962306a36Sopenharmony_ci * 2. Write our commands into our allocated slots in the queue. 51062306a36Sopenharmony_ci * 51162306a36Sopenharmony_ci * 3. Mark our slots as valid in arm_smmu_cmdq.valid_map. 51262306a36Sopenharmony_ci * 51362306a36Sopenharmony_ci * 4. If we are an owner: 51462306a36Sopenharmony_ci * a. Wait for the previous owner to finish. 51562306a36Sopenharmony_ci * b. Mark the queue head as unowned, which tells us the range 51662306a36Sopenharmony_ci * that we are responsible for publishing. 51762306a36Sopenharmony_ci * c. Wait for all commands in our owned range to become valid. 51862306a36Sopenharmony_ci * d. Advance the hardware prod pointer. 51962306a36Sopenharmony_ci * e. Tell the next owner we've finished. 52062306a36Sopenharmony_ci * 52162306a36Sopenharmony_ci * 5. If we are inserting a CMD_SYNC (we may or may not have been an 52262306a36Sopenharmony_ci * owner), then we need to stick around until it has completed: 52362306a36Sopenharmony_ci * a. If we have MSIs, the SMMU can write back into the CMD_SYNC 52462306a36Sopenharmony_ci * to clear the first 4 bytes. 52562306a36Sopenharmony_ci * b. Otherwise, we spin waiting for the hardware cons pointer to 52662306a36Sopenharmony_ci * advance past our command. 52762306a36Sopenharmony_ci * 52862306a36Sopenharmony_ci * The devil is in the details, particularly the use of locking for handling 52962306a36Sopenharmony_ci * SYNC completion and freeing up space in the queue before we think that it is 53062306a36Sopenharmony_ci * full. 53162306a36Sopenharmony_ci */ 53262306a36Sopenharmony_cistatic void __arm_smmu_cmdq_poll_set_valid_map(struct arm_smmu_cmdq *cmdq, 53362306a36Sopenharmony_ci u32 sprod, u32 eprod, bool set) 53462306a36Sopenharmony_ci{ 53562306a36Sopenharmony_ci u32 swidx, sbidx, ewidx, ebidx; 53662306a36Sopenharmony_ci struct arm_smmu_ll_queue llq = { 53762306a36Sopenharmony_ci .max_n_shift = cmdq->q.llq.max_n_shift, 53862306a36Sopenharmony_ci .prod = sprod, 53962306a36Sopenharmony_ci }; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci ewidx = BIT_WORD(Q_IDX(&llq, eprod)); 54262306a36Sopenharmony_ci ebidx = Q_IDX(&llq, eprod) % BITS_PER_LONG; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci while (llq.prod != eprod) { 54562306a36Sopenharmony_ci unsigned long mask; 54662306a36Sopenharmony_ci atomic_long_t *ptr; 54762306a36Sopenharmony_ci u32 limit = BITS_PER_LONG; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci swidx = BIT_WORD(Q_IDX(&llq, llq.prod)); 55062306a36Sopenharmony_ci sbidx = Q_IDX(&llq, llq.prod) % BITS_PER_LONG; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci ptr = &cmdq->valid_map[swidx]; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci if ((swidx == ewidx) && (sbidx < ebidx)) 55562306a36Sopenharmony_ci limit = ebidx; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci mask = GENMASK(limit - 1, sbidx); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci /* 56062306a36Sopenharmony_ci * The valid bit is the inverse of the wrap bit. This means 56162306a36Sopenharmony_ci * that a zero-initialised queue is invalid and, after marking 56262306a36Sopenharmony_ci * all entries as valid, they become invalid again when we 56362306a36Sopenharmony_ci * wrap. 56462306a36Sopenharmony_ci */ 56562306a36Sopenharmony_ci if (set) { 56662306a36Sopenharmony_ci atomic_long_xor(mask, ptr); 56762306a36Sopenharmony_ci } else { /* Poll */ 56862306a36Sopenharmony_ci unsigned long valid; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci valid = (ULONG_MAX + !!Q_WRP(&llq, llq.prod)) & mask; 57162306a36Sopenharmony_ci atomic_long_cond_read_relaxed(ptr, (VAL & mask) == valid); 57262306a36Sopenharmony_ci } 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci llq.prod = queue_inc_prod_n(&llq, limit - sbidx); 57562306a36Sopenharmony_ci } 57662306a36Sopenharmony_ci} 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci/* Mark all entries in the range [sprod, eprod) as valid */ 57962306a36Sopenharmony_cistatic void arm_smmu_cmdq_set_valid_map(struct arm_smmu_cmdq *cmdq, 58062306a36Sopenharmony_ci u32 sprod, u32 eprod) 58162306a36Sopenharmony_ci{ 58262306a36Sopenharmony_ci __arm_smmu_cmdq_poll_set_valid_map(cmdq, sprod, eprod, true); 58362306a36Sopenharmony_ci} 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci/* Wait for all entries in the range [sprod, eprod) to become valid */ 58662306a36Sopenharmony_cistatic void arm_smmu_cmdq_poll_valid_map(struct arm_smmu_cmdq *cmdq, 58762306a36Sopenharmony_ci u32 sprod, u32 eprod) 58862306a36Sopenharmony_ci{ 58962306a36Sopenharmony_ci __arm_smmu_cmdq_poll_set_valid_map(cmdq, sprod, eprod, false); 59062306a36Sopenharmony_ci} 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci/* Wait for the command queue to become non-full */ 59362306a36Sopenharmony_cistatic int arm_smmu_cmdq_poll_until_not_full(struct arm_smmu_device *smmu, 59462306a36Sopenharmony_ci struct arm_smmu_ll_queue *llq) 59562306a36Sopenharmony_ci{ 59662306a36Sopenharmony_ci unsigned long flags; 59762306a36Sopenharmony_ci struct arm_smmu_queue_poll qp; 59862306a36Sopenharmony_ci struct arm_smmu_cmdq *cmdq = arm_smmu_get_cmdq(smmu); 59962306a36Sopenharmony_ci int ret = 0; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci /* 60262306a36Sopenharmony_ci * Try to update our copy of cons by grabbing exclusive cmdq access. If 60362306a36Sopenharmony_ci * that fails, spin until somebody else updates it for us. 60462306a36Sopenharmony_ci */ 60562306a36Sopenharmony_ci if (arm_smmu_cmdq_exclusive_trylock_irqsave(cmdq, flags)) { 60662306a36Sopenharmony_ci WRITE_ONCE(cmdq->q.llq.cons, readl_relaxed(cmdq->q.cons_reg)); 60762306a36Sopenharmony_ci arm_smmu_cmdq_exclusive_unlock_irqrestore(cmdq, flags); 60862306a36Sopenharmony_ci llq->val = READ_ONCE(cmdq->q.llq.val); 60962306a36Sopenharmony_ci return 0; 61062306a36Sopenharmony_ci } 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci queue_poll_init(smmu, &qp); 61362306a36Sopenharmony_ci do { 61462306a36Sopenharmony_ci llq->val = READ_ONCE(cmdq->q.llq.val); 61562306a36Sopenharmony_ci if (!queue_full(llq)) 61662306a36Sopenharmony_ci break; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci ret = queue_poll(&qp); 61962306a36Sopenharmony_ci } while (!ret); 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci return ret; 62262306a36Sopenharmony_ci} 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci/* 62562306a36Sopenharmony_ci * Wait until the SMMU signals a CMD_SYNC completion MSI. 62662306a36Sopenharmony_ci * Must be called with the cmdq lock held in some capacity. 62762306a36Sopenharmony_ci */ 62862306a36Sopenharmony_cistatic int __arm_smmu_cmdq_poll_until_msi(struct arm_smmu_device *smmu, 62962306a36Sopenharmony_ci struct arm_smmu_ll_queue *llq) 63062306a36Sopenharmony_ci{ 63162306a36Sopenharmony_ci int ret = 0; 63262306a36Sopenharmony_ci struct arm_smmu_queue_poll qp; 63362306a36Sopenharmony_ci struct arm_smmu_cmdq *cmdq = arm_smmu_get_cmdq(smmu); 63462306a36Sopenharmony_ci u32 *cmd = (u32 *)(Q_ENT(&cmdq->q, llq->prod)); 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci queue_poll_init(smmu, &qp); 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci /* 63962306a36Sopenharmony_ci * The MSI won't generate an event, since it's being written back 64062306a36Sopenharmony_ci * into the command queue. 64162306a36Sopenharmony_ci */ 64262306a36Sopenharmony_ci qp.wfe = false; 64362306a36Sopenharmony_ci smp_cond_load_relaxed(cmd, !VAL || (ret = queue_poll(&qp))); 64462306a36Sopenharmony_ci llq->cons = ret ? llq->prod : queue_inc_prod_n(llq, 1); 64562306a36Sopenharmony_ci return ret; 64662306a36Sopenharmony_ci} 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci/* 64962306a36Sopenharmony_ci * Wait until the SMMU cons index passes llq->prod. 65062306a36Sopenharmony_ci * Must be called with the cmdq lock held in some capacity. 65162306a36Sopenharmony_ci */ 65262306a36Sopenharmony_cistatic int __arm_smmu_cmdq_poll_until_consumed(struct arm_smmu_device *smmu, 65362306a36Sopenharmony_ci struct arm_smmu_ll_queue *llq) 65462306a36Sopenharmony_ci{ 65562306a36Sopenharmony_ci struct arm_smmu_queue_poll qp; 65662306a36Sopenharmony_ci struct arm_smmu_cmdq *cmdq = arm_smmu_get_cmdq(smmu); 65762306a36Sopenharmony_ci u32 prod = llq->prod; 65862306a36Sopenharmony_ci int ret = 0; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci queue_poll_init(smmu, &qp); 66162306a36Sopenharmony_ci llq->val = READ_ONCE(cmdq->q.llq.val); 66262306a36Sopenharmony_ci do { 66362306a36Sopenharmony_ci if (queue_consumed(llq, prod)) 66462306a36Sopenharmony_ci break; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci ret = queue_poll(&qp); 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci /* 66962306a36Sopenharmony_ci * This needs to be a readl() so that our subsequent call 67062306a36Sopenharmony_ci * to arm_smmu_cmdq_shared_tryunlock() can fail accurately. 67162306a36Sopenharmony_ci * 67262306a36Sopenharmony_ci * Specifically, we need to ensure that we observe all 67362306a36Sopenharmony_ci * shared_lock()s by other CMD_SYNCs that share our owner, 67462306a36Sopenharmony_ci * so that a failing call to tryunlock() means that we're 67562306a36Sopenharmony_ci * the last one out and therefore we can safely advance 67662306a36Sopenharmony_ci * cmdq->q.llq.cons. Roughly speaking: 67762306a36Sopenharmony_ci * 67862306a36Sopenharmony_ci * CPU 0 CPU1 CPU2 (us) 67962306a36Sopenharmony_ci * 68062306a36Sopenharmony_ci * if (sync) 68162306a36Sopenharmony_ci * shared_lock(); 68262306a36Sopenharmony_ci * 68362306a36Sopenharmony_ci * dma_wmb(); 68462306a36Sopenharmony_ci * set_valid_map(); 68562306a36Sopenharmony_ci * 68662306a36Sopenharmony_ci * if (owner) { 68762306a36Sopenharmony_ci * poll_valid_map(); 68862306a36Sopenharmony_ci * <control dependency> 68962306a36Sopenharmony_ci * writel(prod_reg); 69062306a36Sopenharmony_ci * 69162306a36Sopenharmony_ci * readl(cons_reg); 69262306a36Sopenharmony_ci * tryunlock(); 69362306a36Sopenharmony_ci * 69462306a36Sopenharmony_ci * Requires us to see CPU 0's shared_lock() acquisition. 69562306a36Sopenharmony_ci */ 69662306a36Sopenharmony_ci llq->cons = readl(cmdq->q.cons_reg); 69762306a36Sopenharmony_ci } while (!ret); 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci return ret; 70062306a36Sopenharmony_ci} 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_cistatic int arm_smmu_cmdq_poll_until_sync(struct arm_smmu_device *smmu, 70362306a36Sopenharmony_ci struct arm_smmu_ll_queue *llq) 70462306a36Sopenharmony_ci{ 70562306a36Sopenharmony_ci if (smmu->options & ARM_SMMU_OPT_MSIPOLL) 70662306a36Sopenharmony_ci return __arm_smmu_cmdq_poll_until_msi(smmu, llq); 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci return __arm_smmu_cmdq_poll_until_consumed(smmu, llq); 70962306a36Sopenharmony_ci} 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_cistatic void arm_smmu_cmdq_write_entries(struct arm_smmu_cmdq *cmdq, u64 *cmds, 71262306a36Sopenharmony_ci u32 prod, int n) 71362306a36Sopenharmony_ci{ 71462306a36Sopenharmony_ci int i; 71562306a36Sopenharmony_ci struct arm_smmu_ll_queue llq = { 71662306a36Sopenharmony_ci .max_n_shift = cmdq->q.llq.max_n_shift, 71762306a36Sopenharmony_ci .prod = prod, 71862306a36Sopenharmony_ci }; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci for (i = 0; i < n; ++i) { 72162306a36Sopenharmony_ci u64 *cmd = &cmds[i * CMDQ_ENT_DWORDS]; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci prod = queue_inc_prod_n(&llq, i); 72462306a36Sopenharmony_ci queue_write(Q_ENT(&cmdq->q, prod), cmd, CMDQ_ENT_DWORDS); 72562306a36Sopenharmony_ci } 72662306a36Sopenharmony_ci} 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci/* 72962306a36Sopenharmony_ci * This is the actual insertion function, and provides the following 73062306a36Sopenharmony_ci * ordering guarantees to callers: 73162306a36Sopenharmony_ci * 73262306a36Sopenharmony_ci * - There is a dma_wmb() before publishing any commands to the queue. 73362306a36Sopenharmony_ci * This can be relied upon to order prior writes to data structures 73462306a36Sopenharmony_ci * in memory (such as a CD or an STE) before the command. 73562306a36Sopenharmony_ci * 73662306a36Sopenharmony_ci * - On completion of a CMD_SYNC, there is a control dependency. 73762306a36Sopenharmony_ci * This can be relied upon to order subsequent writes to memory (e.g. 73862306a36Sopenharmony_ci * freeing an IOVA) after completion of the CMD_SYNC. 73962306a36Sopenharmony_ci * 74062306a36Sopenharmony_ci * - Command insertion is totally ordered, so if two CPUs each race to 74162306a36Sopenharmony_ci * insert their own list of commands then all of the commands from one 74262306a36Sopenharmony_ci * CPU will appear before any of the commands from the other CPU. 74362306a36Sopenharmony_ci */ 74462306a36Sopenharmony_cistatic int arm_smmu_cmdq_issue_cmdlist(struct arm_smmu_device *smmu, 74562306a36Sopenharmony_ci u64 *cmds, int n, bool sync) 74662306a36Sopenharmony_ci{ 74762306a36Sopenharmony_ci u64 cmd_sync[CMDQ_ENT_DWORDS]; 74862306a36Sopenharmony_ci u32 prod; 74962306a36Sopenharmony_ci unsigned long flags; 75062306a36Sopenharmony_ci bool owner; 75162306a36Sopenharmony_ci struct arm_smmu_cmdq *cmdq = arm_smmu_get_cmdq(smmu); 75262306a36Sopenharmony_ci struct arm_smmu_ll_queue llq, head; 75362306a36Sopenharmony_ci int ret = 0; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci llq.max_n_shift = cmdq->q.llq.max_n_shift; 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci /* 1. Allocate some space in the queue */ 75862306a36Sopenharmony_ci local_irq_save(flags); 75962306a36Sopenharmony_ci llq.val = READ_ONCE(cmdq->q.llq.val); 76062306a36Sopenharmony_ci do { 76162306a36Sopenharmony_ci u64 old; 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci while (!queue_has_space(&llq, n + sync)) { 76462306a36Sopenharmony_ci local_irq_restore(flags); 76562306a36Sopenharmony_ci if (arm_smmu_cmdq_poll_until_not_full(smmu, &llq)) 76662306a36Sopenharmony_ci dev_err_ratelimited(smmu->dev, "CMDQ timeout\n"); 76762306a36Sopenharmony_ci local_irq_save(flags); 76862306a36Sopenharmony_ci } 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci head.cons = llq.cons; 77162306a36Sopenharmony_ci head.prod = queue_inc_prod_n(&llq, n + sync) | 77262306a36Sopenharmony_ci CMDQ_PROD_OWNED_FLAG; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci old = cmpxchg_relaxed(&cmdq->q.llq.val, llq.val, head.val); 77562306a36Sopenharmony_ci if (old == llq.val) 77662306a36Sopenharmony_ci break; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci llq.val = old; 77962306a36Sopenharmony_ci } while (1); 78062306a36Sopenharmony_ci owner = !(llq.prod & CMDQ_PROD_OWNED_FLAG); 78162306a36Sopenharmony_ci head.prod &= ~CMDQ_PROD_OWNED_FLAG; 78262306a36Sopenharmony_ci llq.prod &= ~CMDQ_PROD_OWNED_FLAG; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci /* 78562306a36Sopenharmony_ci * 2. Write our commands into the queue 78662306a36Sopenharmony_ci * Dependency ordering from the cmpxchg() loop above. 78762306a36Sopenharmony_ci */ 78862306a36Sopenharmony_ci arm_smmu_cmdq_write_entries(cmdq, cmds, llq.prod, n); 78962306a36Sopenharmony_ci if (sync) { 79062306a36Sopenharmony_ci prod = queue_inc_prod_n(&llq, n); 79162306a36Sopenharmony_ci arm_smmu_cmdq_build_sync_cmd(cmd_sync, smmu, &cmdq->q, prod); 79262306a36Sopenharmony_ci queue_write(Q_ENT(&cmdq->q, prod), cmd_sync, CMDQ_ENT_DWORDS); 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci /* 79562306a36Sopenharmony_ci * In order to determine completion of our CMD_SYNC, we must 79662306a36Sopenharmony_ci * ensure that the queue can't wrap twice without us noticing. 79762306a36Sopenharmony_ci * We achieve that by taking the cmdq lock as shared before 79862306a36Sopenharmony_ci * marking our slot as valid. 79962306a36Sopenharmony_ci */ 80062306a36Sopenharmony_ci arm_smmu_cmdq_shared_lock(cmdq); 80162306a36Sopenharmony_ci } 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci /* 3. Mark our slots as valid, ensuring commands are visible first */ 80462306a36Sopenharmony_ci dma_wmb(); 80562306a36Sopenharmony_ci arm_smmu_cmdq_set_valid_map(cmdq, llq.prod, head.prod); 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci /* 4. If we are the owner, take control of the SMMU hardware */ 80862306a36Sopenharmony_ci if (owner) { 80962306a36Sopenharmony_ci /* a. Wait for previous owner to finish */ 81062306a36Sopenharmony_ci atomic_cond_read_relaxed(&cmdq->owner_prod, VAL == llq.prod); 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci /* b. Stop gathering work by clearing the owned flag */ 81362306a36Sopenharmony_ci prod = atomic_fetch_andnot_relaxed(CMDQ_PROD_OWNED_FLAG, 81462306a36Sopenharmony_ci &cmdq->q.llq.atomic.prod); 81562306a36Sopenharmony_ci prod &= ~CMDQ_PROD_OWNED_FLAG; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci /* 81862306a36Sopenharmony_ci * c. Wait for any gathered work to be written to the queue. 81962306a36Sopenharmony_ci * Note that we read our own entries so that we have the control 82062306a36Sopenharmony_ci * dependency required by (d). 82162306a36Sopenharmony_ci */ 82262306a36Sopenharmony_ci arm_smmu_cmdq_poll_valid_map(cmdq, llq.prod, prod); 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci /* 82562306a36Sopenharmony_ci * d. Advance the hardware prod pointer 82662306a36Sopenharmony_ci * Control dependency ordering from the entries becoming valid. 82762306a36Sopenharmony_ci */ 82862306a36Sopenharmony_ci writel_relaxed(prod, cmdq->q.prod_reg); 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci /* 83162306a36Sopenharmony_ci * e. Tell the next owner we're done 83262306a36Sopenharmony_ci * Make sure we've updated the hardware first, so that we don't 83362306a36Sopenharmony_ci * race to update prod and potentially move it backwards. 83462306a36Sopenharmony_ci */ 83562306a36Sopenharmony_ci atomic_set_release(&cmdq->owner_prod, prod); 83662306a36Sopenharmony_ci } 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci /* 5. If we are inserting a CMD_SYNC, we must wait for it to complete */ 83962306a36Sopenharmony_ci if (sync) { 84062306a36Sopenharmony_ci llq.prod = queue_inc_prod_n(&llq, n); 84162306a36Sopenharmony_ci ret = arm_smmu_cmdq_poll_until_sync(smmu, &llq); 84262306a36Sopenharmony_ci if (ret) { 84362306a36Sopenharmony_ci dev_err_ratelimited(smmu->dev, 84462306a36Sopenharmony_ci "CMD_SYNC timeout at 0x%08x [hwprod 0x%08x, hwcons 0x%08x]\n", 84562306a36Sopenharmony_ci llq.prod, 84662306a36Sopenharmony_ci readl_relaxed(cmdq->q.prod_reg), 84762306a36Sopenharmony_ci readl_relaxed(cmdq->q.cons_reg)); 84862306a36Sopenharmony_ci } 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci /* 85162306a36Sopenharmony_ci * Try to unlock the cmdq lock. This will fail if we're the last 85262306a36Sopenharmony_ci * reader, in which case we can safely update cmdq->q.llq.cons 85362306a36Sopenharmony_ci */ 85462306a36Sopenharmony_ci if (!arm_smmu_cmdq_shared_tryunlock(cmdq)) { 85562306a36Sopenharmony_ci WRITE_ONCE(cmdq->q.llq.cons, llq.cons); 85662306a36Sopenharmony_ci arm_smmu_cmdq_shared_unlock(cmdq); 85762306a36Sopenharmony_ci } 85862306a36Sopenharmony_ci } 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci local_irq_restore(flags); 86162306a36Sopenharmony_ci return ret; 86262306a36Sopenharmony_ci} 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_cistatic int __arm_smmu_cmdq_issue_cmd(struct arm_smmu_device *smmu, 86562306a36Sopenharmony_ci struct arm_smmu_cmdq_ent *ent, 86662306a36Sopenharmony_ci bool sync) 86762306a36Sopenharmony_ci{ 86862306a36Sopenharmony_ci u64 cmd[CMDQ_ENT_DWORDS]; 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci if (unlikely(arm_smmu_cmdq_build_cmd(cmd, ent))) { 87162306a36Sopenharmony_ci dev_warn(smmu->dev, "ignoring unknown CMDQ opcode 0x%x\n", 87262306a36Sopenharmony_ci ent->opcode); 87362306a36Sopenharmony_ci return -EINVAL; 87462306a36Sopenharmony_ci } 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci return arm_smmu_cmdq_issue_cmdlist(smmu, cmd, 1, sync); 87762306a36Sopenharmony_ci} 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_cistatic int arm_smmu_cmdq_issue_cmd(struct arm_smmu_device *smmu, 88062306a36Sopenharmony_ci struct arm_smmu_cmdq_ent *ent) 88162306a36Sopenharmony_ci{ 88262306a36Sopenharmony_ci return __arm_smmu_cmdq_issue_cmd(smmu, ent, false); 88362306a36Sopenharmony_ci} 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_cistatic int arm_smmu_cmdq_issue_cmd_with_sync(struct arm_smmu_device *smmu, 88662306a36Sopenharmony_ci struct arm_smmu_cmdq_ent *ent) 88762306a36Sopenharmony_ci{ 88862306a36Sopenharmony_ci return __arm_smmu_cmdq_issue_cmd(smmu, ent, true); 88962306a36Sopenharmony_ci} 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_cistatic void arm_smmu_cmdq_batch_add(struct arm_smmu_device *smmu, 89262306a36Sopenharmony_ci struct arm_smmu_cmdq_batch *cmds, 89362306a36Sopenharmony_ci struct arm_smmu_cmdq_ent *cmd) 89462306a36Sopenharmony_ci{ 89562306a36Sopenharmony_ci int index; 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci if (cmds->num == CMDQ_BATCH_ENTRIES - 1 && 89862306a36Sopenharmony_ci (smmu->options & ARM_SMMU_OPT_CMDQ_FORCE_SYNC)) { 89962306a36Sopenharmony_ci arm_smmu_cmdq_issue_cmdlist(smmu, cmds->cmds, cmds->num, true); 90062306a36Sopenharmony_ci cmds->num = 0; 90162306a36Sopenharmony_ci } 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci if (cmds->num == CMDQ_BATCH_ENTRIES) { 90462306a36Sopenharmony_ci arm_smmu_cmdq_issue_cmdlist(smmu, cmds->cmds, cmds->num, false); 90562306a36Sopenharmony_ci cmds->num = 0; 90662306a36Sopenharmony_ci } 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci index = cmds->num * CMDQ_ENT_DWORDS; 90962306a36Sopenharmony_ci if (unlikely(arm_smmu_cmdq_build_cmd(&cmds->cmds[index], cmd))) { 91062306a36Sopenharmony_ci dev_warn(smmu->dev, "ignoring unknown CMDQ opcode 0x%x\n", 91162306a36Sopenharmony_ci cmd->opcode); 91262306a36Sopenharmony_ci return; 91362306a36Sopenharmony_ci } 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci cmds->num++; 91662306a36Sopenharmony_ci} 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_cistatic int arm_smmu_cmdq_batch_submit(struct arm_smmu_device *smmu, 91962306a36Sopenharmony_ci struct arm_smmu_cmdq_batch *cmds) 92062306a36Sopenharmony_ci{ 92162306a36Sopenharmony_ci return arm_smmu_cmdq_issue_cmdlist(smmu, cmds->cmds, cmds->num, true); 92262306a36Sopenharmony_ci} 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_cistatic int arm_smmu_page_response(struct device *dev, 92562306a36Sopenharmony_ci struct iommu_fault_event *unused, 92662306a36Sopenharmony_ci struct iommu_page_response *resp) 92762306a36Sopenharmony_ci{ 92862306a36Sopenharmony_ci struct arm_smmu_cmdq_ent cmd = {0}; 92962306a36Sopenharmony_ci struct arm_smmu_master *master = dev_iommu_priv_get(dev); 93062306a36Sopenharmony_ci int sid = master->streams[0].id; 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci if (master->stall_enabled) { 93362306a36Sopenharmony_ci cmd.opcode = CMDQ_OP_RESUME; 93462306a36Sopenharmony_ci cmd.resume.sid = sid; 93562306a36Sopenharmony_ci cmd.resume.stag = resp->grpid; 93662306a36Sopenharmony_ci switch (resp->code) { 93762306a36Sopenharmony_ci case IOMMU_PAGE_RESP_INVALID: 93862306a36Sopenharmony_ci case IOMMU_PAGE_RESP_FAILURE: 93962306a36Sopenharmony_ci cmd.resume.resp = CMDQ_RESUME_0_RESP_ABORT; 94062306a36Sopenharmony_ci break; 94162306a36Sopenharmony_ci case IOMMU_PAGE_RESP_SUCCESS: 94262306a36Sopenharmony_ci cmd.resume.resp = CMDQ_RESUME_0_RESP_RETRY; 94362306a36Sopenharmony_ci break; 94462306a36Sopenharmony_ci default: 94562306a36Sopenharmony_ci return -EINVAL; 94662306a36Sopenharmony_ci } 94762306a36Sopenharmony_ci } else { 94862306a36Sopenharmony_ci return -ENODEV; 94962306a36Sopenharmony_ci } 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci arm_smmu_cmdq_issue_cmd(master->smmu, &cmd); 95262306a36Sopenharmony_ci /* 95362306a36Sopenharmony_ci * Don't send a SYNC, it doesn't do anything for RESUME or PRI_RESP. 95462306a36Sopenharmony_ci * RESUME consumption guarantees that the stalled transaction will be 95562306a36Sopenharmony_ci * terminated... at some point in the future. PRI_RESP is fire and 95662306a36Sopenharmony_ci * forget. 95762306a36Sopenharmony_ci */ 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci return 0; 96062306a36Sopenharmony_ci} 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci/* Context descriptor manipulation functions */ 96362306a36Sopenharmony_civoid arm_smmu_tlb_inv_asid(struct arm_smmu_device *smmu, u16 asid) 96462306a36Sopenharmony_ci{ 96562306a36Sopenharmony_ci struct arm_smmu_cmdq_ent cmd = { 96662306a36Sopenharmony_ci .opcode = smmu->features & ARM_SMMU_FEAT_E2H ? 96762306a36Sopenharmony_ci CMDQ_OP_TLBI_EL2_ASID : CMDQ_OP_TLBI_NH_ASID, 96862306a36Sopenharmony_ci .tlbi.asid = asid, 96962306a36Sopenharmony_ci }; 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci arm_smmu_cmdq_issue_cmd_with_sync(smmu, &cmd); 97262306a36Sopenharmony_ci} 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_cistatic void arm_smmu_sync_cd(struct arm_smmu_domain *smmu_domain, 97562306a36Sopenharmony_ci int ssid, bool leaf) 97662306a36Sopenharmony_ci{ 97762306a36Sopenharmony_ci size_t i; 97862306a36Sopenharmony_ci unsigned long flags; 97962306a36Sopenharmony_ci struct arm_smmu_master *master; 98062306a36Sopenharmony_ci struct arm_smmu_cmdq_batch cmds; 98162306a36Sopenharmony_ci struct arm_smmu_device *smmu = smmu_domain->smmu; 98262306a36Sopenharmony_ci struct arm_smmu_cmdq_ent cmd = { 98362306a36Sopenharmony_ci .opcode = CMDQ_OP_CFGI_CD, 98462306a36Sopenharmony_ci .cfgi = { 98562306a36Sopenharmony_ci .ssid = ssid, 98662306a36Sopenharmony_ci .leaf = leaf, 98762306a36Sopenharmony_ci }, 98862306a36Sopenharmony_ci }; 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci cmds.num = 0; 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci spin_lock_irqsave(&smmu_domain->devices_lock, flags); 99362306a36Sopenharmony_ci list_for_each_entry(master, &smmu_domain->devices, domain_head) { 99462306a36Sopenharmony_ci for (i = 0; i < master->num_streams; i++) { 99562306a36Sopenharmony_ci cmd.cfgi.sid = master->streams[i].id; 99662306a36Sopenharmony_ci arm_smmu_cmdq_batch_add(smmu, &cmds, &cmd); 99762306a36Sopenharmony_ci } 99862306a36Sopenharmony_ci } 99962306a36Sopenharmony_ci spin_unlock_irqrestore(&smmu_domain->devices_lock, flags); 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci arm_smmu_cmdq_batch_submit(smmu, &cmds); 100262306a36Sopenharmony_ci} 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_cistatic int arm_smmu_alloc_cd_leaf_table(struct arm_smmu_device *smmu, 100562306a36Sopenharmony_ci struct arm_smmu_l1_ctx_desc *l1_desc) 100662306a36Sopenharmony_ci{ 100762306a36Sopenharmony_ci size_t size = CTXDESC_L2_ENTRIES * (CTXDESC_CD_DWORDS << 3); 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci l1_desc->l2ptr = dmam_alloc_coherent(smmu->dev, size, 101062306a36Sopenharmony_ci &l1_desc->l2ptr_dma, GFP_KERNEL); 101162306a36Sopenharmony_ci if (!l1_desc->l2ptr) { 101262306a36Sopenharmony_ci dev_warn(smmu->dev, 101362306a36Sopenharmony_ci "failed to allocate context descriptor table\n"); 101462306a36Sopenharmony_ci return -ENOMEM; 101562306a36Sopenharmony_ci } 101662306a36Sopenharmony_ci return 0; 101762306a36Sopenharmony_ci} 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_cistatic void arm_smmu_write_cd_l1_desc(__le64 *dst, 102062306a36Sopenharmony_ci struct arm_smmu_l1_ctx_desc *l1_desc) 102162306a36Sopenharmony_ci{ 102262306a36Sopenharmony_ci u64 val = (l1_desc->l2ptr_dma & CTXDESC_L1_DESC_L2PTR_MASK) | 102362306a36Sopenharmony_ci CTXDESC_L1_DESC_V; 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci /* See comment in arm_smmu_write_ctx_desc() */ 102662306a36Sopenharmony_ci WRITE_ONCE(*dst, cpu_to_le64(val)); 102762306a36Sopenharmony_ci} 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_cistatic __le64 *arm_smmu_get_cd_ptr(struct arm_smmu_domain *smmu_domain, 103062306a36Sopenharmony_ci u32 ssid) 103162306a36Sopenharmony_ci{ 103262306a36Sopenharmony_ci __le64 *l1ptr; 103362306a36Sopenharmony_ci unsigned int idx; 103462306a36Sopenharmony_ci struct arm_smmu_l1_ctx_desc *l1_desc; 103562306a36Sopenharmony_ci struct arm_smmu_device *smmu = smmu_domain->smmu; 103662306a36Sopenharmony_ci struct arm_smmu_ctx_desc_cfg *cdcfg = &smmu_domain->s1_cfg.cdcfg; 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci if (smmu_domain->s1_cfg.s1fmt == STRTAB_STE_0_S1FMT_LINEAR) 103962306a36Sopenharmony_ci return cdcfg->cdtab + ssid * CTXDESC_CD_DWORDS; 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci idx = ssid >> CTXDESC_SPLIT; 104262306a36Sopenharmony_ci l1_desc = &cdcfg->l1_desc[idx]; 104362306a36Sopenharmony_ci if (!l1_desc->l2ptr) { 104462306a36Sopenharmony_ci if (arm_smmu_alloc_cd_leaf_table(smmu, l1_desc)) 104562306a36Sopenharmony_ci return NULL; 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci l1ptr = cdcfg->cdtab + idx * CTXDESC_L1_DESC_DWORDS; 104862306a36Sopenharmony_ci arm_smmu_write_cd_l1_desc(l1ptr, l1_desc); 104962306a36Sopenharmony_ci /* An invalid L1CD can be cached */ 105062306a36Sopenharmony_ci arm_smmu_sync_cd(smmu_domain, ssid, false); 105162306a36Sopenharmony_ci } 105262306a36Sopenharmony_ci idx = ssid & (CTXDESC_L2_ENTRIES - 1); 105362306a36Sopenharmony_ci return l1_desc->l2ptr + idx * CTXDESC_CD_DWORDS; 105462306a36Sopenharmony_ci} 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ciint arm_smmu_write_ctx_desc(struct arm_smmu_domain *smmu_domain, int ssid, 105762306a36Sopenharmony_ci struct arm_smmu_ctx_desc *cd) 105862306a36Sopenharmony_ci{ 105962306a36Sopenharmony_ci /* 106062306a36Sopenharmony_ci * This function handles the following cases: 106162306a36Sopenharmony_ci * 106262306a36Sopenharmony_ci * (1) Install primary CD, for normal DMA traffic (SSID = IOMMU_NO_PASID = 0). 106362306a36Sopenharmony_ci * (2) Install a secondary CD, for SID+SSID traffic. 106462306a36Sopenharmony_ci * (3) Update ASID of a CD. Atomically write the first 64 bits of the 106562306a36Sopenharmony_ci * CD, then invalidate the old entry and mappings. 106662306a36Sopenharmony_ci * (4) Quiesce the context without clearing the valid bit. Disable 106762306a36Sopenharmony_ci * translation, and ignore any translation fault. 106862306a36Sopenharmony_ci * (5) Remove a secondary CD. 106962306a36Sopenharmony_ci */ 107062306a36Sopenharmony_ci u64 val; 107162306a36Sopenharmony_ci bool cd_live; 107262306a36Sopenharmony_ci __le64 *cdptr; 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci if (WARN_ON(ssid >= (1 << smmu_domain->s1_cfg.s1cdmax))) 107562306a36Sopenharmony_ci return -E2BIG; 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci cdptr = arm_smmu_get_cd_ptr(smmu_domain, ssid); 107862306a36Sopenharmony_ci if (!cdptr) 107962306a36Sopenharmony_ci return -ENOMEM; 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci val = le64_to_cpu(cdptr[0]); 108262306a36Sopenharmony_ci cd_live = !!(val & CTXDESC_CD_0_V); 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci if (!cd) { /* (5) */ 108562306a36Sopenharmony_ci val = 0; 108662306a36Sopenharmony_ci } else if (cd == &quiet_cd) { /* (4) */ 108762306a36Sopenharmony_ci val |= CTXDESC_CD_0_TCR_EPD0; 108862306a36Sopenharmony_ci } else if (cd_live) { /* (3) */ 108962306a36Sopenharmony_ci val &= ~CTXDESC_CD_0_ASID; 109062306a36Sopenharmony_ci val |= FIELD_PREP(CTXDESC_CD_0_ASID, cd->asid); 109162306a36Sopenharmony_ci /* 109262306a36Sopenharmony_ci * Until CD+TLB invalidation, both ASIDs may be used for tagging 109362306a36Sopenharmony_ci * this substream's traffic 109462306a36Sopenharmony_ci */ 109562306a36Sopenharmony_ci } else { /* (1) and (2) */ 109662306a36Sopenharmony_ci cdptr[1] = cpu_to_le64(cd->ttbr & CTXDESC_CD_1_TTB0_MASK); 109762306a36Sopenharmony_ci cdptr[2] = 0; 109862306a36Sopenharmony_ci cdptr[3] = cpu_to_le64(cd->mair); 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci /* 110162306a36Sopenharmony_ci * STE is live, and the SMMU might read dwords of this CD in any 110262306a36Sopenharmony_ci * order. Ensure that it observes valid values before reading 110362306a36Sopenharmony_ci * V=1. 110462306a36Sopenharmony_ci */ 110562306a36Sopenharmony_ci arm_smmu_sync_cd(smmu_domain, ssid, true); 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci val = cd->tcr | 110862306a36Sopenharmony_ci#ifdef __BIG_ENDIAN 110962306a36Sopenharmony_ci CTXDESC_CD_0_ENDI | 111062306a36Sopenharmony_ci#endif 111162306a36Sopenharmony_ci CTXDESC_CD_0_R | CTXDESC_CD_0_A | 111262306a36Sopenharmony_ci (cd->mm ? 0 : CTXDESC_CD_0_ASET) | 111362306a36Sopenharmony_ci CTXDESC_CD_0_AA64 | 111462306a36Sopenharmony_ci FIELD_PREP(CTXDESC_CD_0_ASID, cd->asid) | 111562306a36Sopenharmony_ci CTXDESC_CD_0_V; 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci if (smmu_domain->stall_enabled) 111862306a36Sopenharmony_ci val |= CTXDESC_CD_0_S; 111962306a36Sopenharmony_ci } 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci /* 112262306a36Sopenharmony_ci * The SMMU accesses 64-bit values atomically. See IHI0070Ca 3.21.3 112362306a36Sopenharmony_ci * "Configuration structures and configuration invalidation completion" 112462306a36Sopenharmony_ci * 112562306a36Sopenharmony_ci * The size of single-copy atomic reads made by the SMMU is 112662306a36Sopenharmony_ci * IMPLEMENTATION DEFINED but must be at least 64 bits. Any single 112762306a36Sopenharmony_ci * field within an aligned 64-bit span of a structure can be altered 112862306a36Sopenharmony_ci * without first making the structure invalid. 112962306a36Sopenharmony_ci */ 113062306a36Sopenharmony_ci WRITE_ONCE(cdptr[0], cpu_to_le64(val)); 113162306a36Sopenharmony_ci arm_smmu_sync_cd(smmu_domain, ssid, true); 113262306a36Sopenharmony_ci return 0; 113362306a36Sopenharmony_ci} 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_cistatic int arm_smmu_alloc_cd_tables(struct arm_smmu_domain *smmu_domain) 113662306a36Sopenharmony_ci{ 113762306a36Sopenharmony_ci int ret; 113862306a36Sopenharmony_ci size_t l1size; 113962306a36Sopenharmony_ci size_t max_contexts; 114062306a36Sopenharmony_ci struct arm_smmu_device *smmu = smmu_domain->smmu; 114162306a36Sopenharmony_ci struct arm_smmu_s1_cfg *cfg = &smmu_domain->s1_cfg; 114262306a36Sopenharmony_ci struct arm_smmu_ctx_desc_cfg *cdcfg = &cfg->cdcfg; 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci max_contexts = 1 << cfg->s1cdmax; 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci if (!(smmu->features & ARM_SMMU_FEAT_2_LVL_CDTAB) || 114762306a36Sopenharmony_ci max_contexts <= CTXDESC_L2_ENTRIES) { 114862306a36Sopenharmony_ci cfg->s1fmt = STRTAB_STE_0_S1FMT_LINEAR; 114962306a36Sopenharmony_ci cdcfg->num_l1_ents = max_contexts; 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci l1size = max_contexts * (CTXDESC_CD_DWORDS << 3); 115262306a36Sopenharmony_ci } else { 115362306a36Sopenharmony_ci cfg->s1fmt = STRTAB_STE_0_S1FMT_64K_L2; 115462306a36Sopenharmony_ci cdcfg->num_l1_ents = DIV_ROUND_UP(max_contexts, 115562306a36Sopenharmony_ci CTXDESC_L2_ENTRIES); 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci cdcfg->l1_desc = devm_kcalloc(smmu->dev, cdcfg->num_l1_ents, 115862306a36Sopenharmony_ci sizeof(*cdcfg->l1_desc), 115962306a36Sopenharmony_ci GFP_KERNEL); 116062306a36Sopenharmony_ci if (!cdcfg->l1_desc) 116162306a36Sopenharmony_ci return -ENOMEM; 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci l1size = cdcfg->num_l1_ents * (CTXDESC_L1_DESC_DWORDS << 3); 116462306a36Sopenharmony_ci } 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci cdcfg->cdtab = dmam_alloc_coherent(smmu->dev, l1size, &cdcfg->cdtab_dma, 116762306a36Sopenharmony_ci GFP_KERNEL); 116862306a36Sopenharmony_ci if (!cdcfg->cdtab) { 116962306a36Sopenharmony_ci dev_warn(smmu->dev, "failed to allocate context descriptor\n"); 117062306a36Sopenharmony_ci ret = -ENOMEM; 117162306a36Sopenharmony_ci goto err_free_l1; 117262306a36Sopenharmony_ci } 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci return 0; 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_cierr_free_l1: 117762306a36Sopenharmony_ci if (cdcfg->l1_desc) { 117862306a36Sopenharmony_ci devm_kfree(smmu->dev, cdcfg->l1_desc); 117962306a36Sopenharmony_ci cdcfg->l1_desc = NULL; 118062306a36Sopenharmony_ci } 118162306a36Sopenharmony_ci return ret; 118262306a36Sopenharmony_ci} 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_cistatic void arm_smmu_free_cd_tables(struct arm_smmu_domain *smmu_domain) 118562306a36Sopenharmony_ci{ 118662306a36Sopenharmony_ci int i; 118762306a36Sopenharmony_ci size_t size, l1size; 118862306a36Sopenharmony_ci struct arm_smmu_device *smmu = smmu_domain->smmu; 118962306a36Sopenharmony_ci struct arm_smmu_ctx_desc_cfg *cdcfg = &smmu_domain->s1_cfg.cdcfg; 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci if (cdcfg->l1_desc) { 119262306a36Sopenharmony_ci size = CTXDESC_L2_ENTRIES * (CTXDESC_CD_DWORDS << 3); 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci for (i = 0; i < cdcfg->num_l1_ents; i++) { 119562306a36Sopenharmony_ci if (!cdcfg->l1_desc[i].l2ptr) 119662306a36Sopenharmony_ci continue; 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci dmam_free_coherent(smmu->dev, size, 119962306a36Sopenharmony_ci cdcfg->l1_desc[i].l2ptr, 120062306a36Sopenharmony_ci cdcfg->l1_desc[i].l2ptr_dma); 120162306a36Sopenharmony_ci } 120262306a36Sopenharmony_ci devm_kfree(smmu->dev, cdcfg->l1_desc); 120362306a36Sopenharmony_ci cdcfg->l1_desc = NULL; 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci l1size = cdcfg->num_l1_ents * (CTXDESC_L1_DESC_DWORDS << 3); 120662306a36Sopenharmony_ci } else { 120762306a36Sopenharmony_ci l1size = cdcfg->num_l1_ents * (CTXDESC_CD_DWORDS << 3); 120862306a36Sopenharmony_ci } 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci dmam_free_coherent(smmu->dev, l1size, cdcfg->cdtab, cdcfg->cdtab_dma); 121162306a36Sopenharmony_ci cdcfg->cdtab_dma = 0; 121262306a36Sopenharmony_ci cdcfg->cdtab = NULL; 121362306a36Sopenharmony_ci} 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_cibool arm_smmu_free_asid(struct arm_smmu_ctx_desc *cd) 121662306a36Sopenharmony_ci{ 121762306a36Sopenharmony_ci bool free; 121862306a36Sopenharmony_ci struct arm_smmu_ctx_desc *old_cd; 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci if (!cd->asid) 122162306a36Sopenharmony_ci return false; 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci free = refcount_dec_and_test(&cd->refs); 122462306a36Sopenharmony_ci if (free) { 122562306a36Sopenharmony_ci old_cd = xa_erase(&arm_smmu_asid_xa, cd->asid); 122662306a36Sopenharmony_ci WARN_ON(old_cd != cd); 122762306a36Sopenharmony_ci } 122862306a36Sopenharmony_ci return free; 122962306a36Sopenharmony_ci} 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci/* Stream table manipulation functions */ 123262306a36Sopenharmony_cistatic void 123362306a36Sopenharmony_ciarm_smmu_write_strtab_l1_desc(__le64 *dst, struct arm_smmu_strtab_l1_desc *desc) 123462306a36Sopenharmony_ci{ 123562306a36Sopenharmony_ci u64 val = 0; 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci val |= FIELD_PREP(STRTAB_L1_DESC_SPAN, desc->span); 123862306a36Sopenharmony_ci val |= desc->l2ptr_dma & STRTAB_L1_DESC_L2PTR_MASK; 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci /* See comment in arm_smmu_write_ctx_desc() */ 124162306a36Sopenharmony_ci WRITE_ONCE(*dst, cpu_to_le64(val)); 124262306a36Sopenharmony_ci} 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_cistatic void arm_smmu_sync_ste_for_sid(struct arm_smmu_device *smmu, u32 sid) 124562306a36Sopenharmony_ci{ 124662306a36Sopenharmony_ci struct arm_smmu_cmdq_ent cmd = { 124762306a36Sopenharmony_ci .opcode = CMDQ_OP_CFGI_STE, 124862306a36Sopenharmony_ci .cfgi = { 124962306a36Sopenharmony_ci .sid = sid, 125062306a36Sopenharmony_ci .leaf = true, 125162306a36Sopenharmony_ci }, 125262306a36Sopenharmony_ci }; 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci arm_smmu_cmdq_issue_cmd_with_sync(smmu, &cmd); 125562306a36Sopenharmony_ci} 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_cistatic void arm_smmu_write_strtab_ent(struct arm_smmu_master *master, u32 sid, 125862306a36Sopenharmony_ci __le64 *dst) 125962306a36Sopenharmony_ci{ 126062306a36Sopenharmony_ci /* 126162306a36Sopenharmony_ci * This is hideously complicated, but we only really care about 126262306a36Sopenharmony_ci * three cases at the moment: 126362306a36Sopenharmony_ci * 126462306a36Sopenharmony_ci * 1. Invalid (all zero) -> bypass/fault (init) 126562306a36Sopenharmony_ci * 2. Bypass/fault -> translation/bypass (attach) 126662306a36Sopenharmony_ci * 3. Translation/bypass -> bypass/fault (detach) 126762306a36Sopenharmony_ci * 126862306a36Sopenharmony_ci * Given that we can't update the STE atomically and the SMMU 126962306a36Sopenharmony_ci * doesn't read the thing in a defined order, that leaves us 127062306a36Sopenharmony_ci * with the following maintenance requirements: 127162306a36Sopenharmony_ci * 127262306a36Sopenharmony_ci * 1. Update Config, return (init time STEs aren't live) 127362306a36Sopenharmony_ci * 2. Write everything apart from dword 0, sync, write dword 0, sync 127462306a36Sopenharmony_ci * 3. Update Config, sync 127562306a36Sopenharmony_ci */ 127662306a36Sopenharmony_ci u64 val = le64_to_cpu(dst[0]); 127762306a36Sopenharmony_ci bool ste_live = false; 127862306a36Sopenharmony_ci struct arm_smmu_device *smmu = NULL; 127962306a36Sopenharmony_ci struct arm_smmu_s1_cfg *s1_cfg = NULL; 128062306a36Sopenharmony_ci struct arm_smmu_s2_cfg *s2_cfg = NULL; 128162306a36Sopenharmony_ci struct arm_smmu_domain *smmu_domain = NULL; 128262306a36Sopenharmony_ci struct arm_smmu_cmdq_ent prefetch_cmd = { 128362306a36Sopenharmony_ci .opcode = CMDQ_OP_PREFETCH_CFG, 128462306a36Sopenharmony_ci .prefetch = { 128562306a36Sopenharmony_ci .sid = sid, 128662306a36Sopenharmony_ci }, 128762306a36Sopenharmony_ci }; 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci if (master) { 129062306a36Sopenharmony_ci smmu_domain = master->domain; 129162306a36Sopenharmony_ci smmu = master->smmu; 129262306a36Sopenharmony_ci } 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci if (smmu_domain) { 129562306a36Sopenharmony_ci switch (smmu_domain->stage) { 129662306a36Sopenharmony_ci case ARM_SMMU_DOMAIN_S1: 129762306a36Sopenharmony_ci s1_cfg = &smmu_domain->s1_cfg; 129862306a36Sopenharmony_ci break; 129962306a36Sopenharmony_ci case ARM_SMMU_DOMAIN_S2: 130062306a36Sopenharmony_ci case ARM_SMMU_DOMAIN_NESTED: 130162306a36Sopenharmony_ci s2_cfg = &smmu_domain->s2_cfg; 130262306a36Sopenharmony_ci break; 130362306a36Sopenharmony_ci default: 130462306a36Sopenharmony_ci break; 130562306a36Sopenharmony_ci } 130662306a36Sopenharmony_ci } 130762306a36Sopenharmony_ci 130862306a36Sopenharmony_ci if (val & STRTAB_STE_0_V) { 130962306a36Sopenharmony_ci switch (FIELD_GET(STRTAB_STE_0_CFG, val)) { 131062306a36Sopenharmony_ci case STRTAB_STE_0_CFG_BYPASS: 131162306a36Sopenharmony_ci break; 131262306a36Sopenharmony_ci case STRTAB_STE_0_CFG_S1_TRANS: 131362306a36Sopenharmony_ci case STRTAB_STE_0_CFG_S2_TRANS: 131462306a36Sopenharmony_ci ste_live = true; 131562306a36Sopenharmony_ci break; 131662306a36Sopenharmony_ci case STRTAB_STE_0_CFG_ABORT: 131762306a36Sopenharmony_ci BUG_ON(!disable_bypass); 131862306a36Sopenharmony_ci break; 131962306a36Sopenharmony_ci default: 132062306a36Sopenharmony_ci BUG(); /* STE corruption */ 132162306a36Sopenharmony_ci } 132262306a36Sopenharmony_ci } 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci /* Nuke the existing STE_0 value, as we're going to rewrite it */ 132562306a36Sopenharmony_ci val = STRTAB_STE_0_V; 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_ci /* Bypass/fault */ 132862306a36Sopenharmony_ci if (!smmu_domain || !(s1_cfg || s2_cfg)) { 132962306a36Sopenharmony_ci if (!smmu_domain && disable_bypass) 133062306a36Sopenharmony_ci val |= FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_ABORT); 133162306a36Sopenharmony_ci else 133262306a36Sopenharmony_ci val |= FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_BYPASS); 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_ci dst[0] = cpu_to_le64(val); 133562306a36Sopenharmony_ci dst[1] = cpu_to_le64(FIELD_PREP(STRTAB_STE_1_SHCFG, 133662306a36Sopenharmony_ci STRTAB_STE_1_SHCFG_INCOMING)); 133762306a36Sopenharmony_ci dst[2] = 0; /* Nuke the VMID */ 133862306a36Sopenharmony_ci /* 133962306a36Sopenharmony_ci * The SMMU can perform negative caching, so we must sync 134062306a36Sopenharmony_ci * the STE regardless of whether the old value was live. 134162306a36Sopenharmony_ci */ 134262306a36Sopenharmony_ci if (smmu) 134362306a36Sopenharmony_ci arm_smmu_sync_ste_for_sid(smmu, sid); 134462306a36Sopenharmony_ci return; 134562306a36Sopenharmony_ci } 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_ci if (s1_cfg) { 134862306a36Sopenharmony_ci u64 strw = smmu->features & ARM_SMMU_FEAT_E2H ? 134962306a36Sopenharmony_ci STRTAB_STE_1_STRW_EL2 : STRTAB_STE_1_STRW_NSEL1; 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_ci BUG_ON(ste_live); 135262306a36Sopenharmony_ci dst[1] = cpu_to_le64( 135362306a36Sopenharmony_ci FIELD_PREP(STRTAB_STE_1_S1DSS, STRTAB_STE_1_S1DSS_SSID0) | 135462306a36Sopenharmony_ci FIELD_PREP(STRTAB_STE_1_S1CIR, STRTAB_STE_1_S1C_CACHE_WBRA) | 135562306a36Sopenharmony_ci FIELD_PREP(STRTAB_STE_1_S1COR, STRTAB_STE_1_S1C_CACHE_WBRA) | 135662306a36Sopenharmony_ci FIELD_PREP(STRTAB_STE_1_S1CSH, ARM_SMMU_SH_ISH) | 135762306a36Sopenharmony_ci FIELD_PREP(STRTAB_STE_1_STRW, strw)); 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_STALLS && 136062306a36Sopenharmony_ci !master->stall_enabled) 136162306a36Sopenharmony_ci dst[1] |= cpu_to_le64(STRTAB_STE_1_S1STALLD); 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_ci val |= (s1_cfg->cdcfg.cdtab_dma & STRTAB_STE_0_S1CTXPTR_MASK) | 136462306a36Sopenharmony_ci FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_S1_TRANS) | 136562306a36Sopenharmony_ci FIELD_PREP(STRTAB_STE_0_S1CDMAX, s1_cfg->s1cdmax) | 136662306a36Sopenharmony_ci FIELD_PREP(STRTAB_STE_0_S1FMT, s1_cfg->s1fmt); 136762306a36Sopenharmony_ci } 136862306a36Sopenharmony_ci 136962306a36Sopenharmony_ci if (s2_cfg) { 137062306a36Sopenharmony_ci BUG_ON(ste_live); 137162306a36Sopenharmony_ci dst[2] = cpu_to_le64( 137262306a36Sopenharmony_ci FIELD_PREP(STRTAB_STE_2_S2VMID, s2_cfg->vmid) | 137362306a36Sopenharmony_ci FIELD_PREP(STRTAB_STE_2_VTCR, s2_cfg->vtcr) | 137462306a36Sopenharmony_ci#ifdef __BIG_ENDIAN 137562306a36Sopenharmony_ci STRTAB_STE_2_S2ENDI | 137662306a36Sopenharmony_ci#endif 137762306a36Sopenharmony_ci STRTAB_STE_2_S2PTW | STRTAB_STE_2_S2AA64 | 137862306a36Sopenharmony_ci STRTAB_STE_2_S2R); 137962306a36Sopenharmony_ci 138062306a36Sopenharmony_ci dst[3] = cpu_to_le64(s2_cfg->vttbr & STRTAB_STE_3_S2TTB_MASK); 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_ci val |= FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_S2_TRANS); 138362306a36Sopenharmony_ci } 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_ci if (master->ats_enabled) 138662306a36Sopenharmony_ci dst[1] |= cpu_to_le64(FIELD_PREP(STRTAB_STE_1_EATS, 138762306a36Sopenharmony_ci STRTAB_STE_1_EATS_TRANS)); 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci arm_smmu_sync_ste_for_sid(smmu, sid); 139062306a36Sopenharmony_ci /* See comment in arm_smmu_write_ctx_desc() */ 139162306a36Sopenharmony_ci WRITE_ONCE(dst[0], cpu_to_le64(val)); 139262306a36Sopenharmony_ci arm_smmu_sync_ste_for_sid(smmu, sid); 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_ci /* It's likely that we'll want to use the new STE soon */ 139562306a36Sopenharmony_ci if (!(smmu->options & ARM_SMMU_OPT_SKIP_PREFETCH)) 139662306a36Sopenharmony_ci arm_smmu_cmdq_issue_cmd(smmu, &prefetch_cmd); 139762306a36Sopenharmony_ci} 139862306a36Sopenharmony_ci 139962306a36Sopenharmony_cistatic void arm_smmu_init_bypass_stes(__le64 *strtab, unsigned int nent, bool force) 140062306a36Sopenharmony_ci{ 140162306a36Sopenharmony_ci unsigned int i; 140262306a36Sopenharmony_ci u64 val = STRTAB_STE_0_V; 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ci if (disable_bypass && !force) 140562306a36Sopenharmony_ci val |= FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_ABORT); 140662306a36Sopenharmony_ci else 140762306a36Sopenharmony_ci val |= FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_BYPASS); 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_ci for (i = 0; i < nent; ++i) { 141062306a36Sopenharmony_ci strtab[0] = cpu_to_le64(val); 141162306a36Sopenharmony_ci strtab[1] = cpu_to_le64(FIELD_PREP(STRTAB_STE_1_SHCFG, 141262306a36Sopenharmony_ci STRTAB_STE_1_SHCFG_INCOMING)); 141362306a36Sopenharmony_ci strtab[2] = 0; 141462306a36Sopenharmony_ci strtab += STRTAB_STE_DWORDS; 141562306a36Sopenharmony_ci } 141662306a36Sopenharmony_ci} 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_cistatic int arm_smmu_init_l2_strtab(struct arm_smmu_device *smmu, u32 sid) 141962306a36Sopenharmony_ci{ 142062306a36Sopenharmony_ci size_t size; 142162306a36Sopenharmony_ci void *strtab; 142262306a36Sopenharmony_ci struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg; 142362306a36Sopenharmony_ci struct arm_smmu_strtab_l1_desc *desc = &cfg->l1_desc[sid >> STRTAB_SPLIT]; 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_ci if (desc->l2ptr) 142662306a36Sopenharmony_ci return 0; 142762306a36Sopenharmony_ci 142862306a36Sopenharmony_ci size = 1 << (STRTAB_SPLIT + ilog2(STRTAB_STE_DWORDS) + 3); 142962306a36Sopenharmony_ci strtab = &cfg->strtab[(sid >> STRTAB_SPLIT) * STRTAB_L1_DESC_DWORDS]; 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_ci desc->span = STRTAB_SPLIT + 1; 143262306a36Sopenharmony_ci desc->l2ptr = dmam_alloc_coherent(smmu->dev, size, &desc->l2ptr_dma, 143362306a36Sopenharmony_ci GFP_KERNEL); 143462306a36Sopenharmony_ci if (!desc->l2ptr) { 143562306a36Sopenharmony_ci dev_err(smmu->dev, 143662306a36Sopenharmony_ci "failed to allocate l2 stream table for SID %u\n", 143762306a36Sopenharmony_ci sid); 143862306a36Sopenharmony_ci return -ENOMEM; 143962306a36Sopenharmony_ci } 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_ci arm_smmu_init_bypass_stes(desc->l2ptr, 1 << STRTAB_SPLIT, false); 144262306a36Sopenharmony_ci arm_smmu_write_strtab_l1_desc(strtab, desc); 144362306a36Sopenharmony_ci return 0; 144462306a36Sopenharmony_ci} 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_cistatic struct arm_smmu_master * 144762306a36Sopenharmony_ciarm_smmu_find_master(struct arm_smmu_device *smmu, u32 sid) 144862306a36Sopenharmony_ci{ 144962306a36Sopenharmony_ci struct rb_node *node; 145062306a36Sopenharmony_ci struct arm_smmu_stream *stream; 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_ci lockdep_assert_held(&smmu->streams_mutex); 145362306a36Sopenharmony_ci 145462306a36Sopenharmony_ci node = smmu->streams.rb_node; 145562306a36Sopenharmony_ci while (node) { 145662306a36Sopenharmony_ci stream = rb_entry(node, struct arm_smmu_stream, node); 145762306a36Sopenharmony_ci if (stream->id < sid) 145862306a36Sopenharmony_ci node = node->rb_right; 145962306a36Sopenharmony_ci else if (stream->id > sid) 146062306a36Sopenharmony_ci node = node->rb_left; 146162306a36Sopenharmony_ci else 146262306a36Sopenharmony_ci return stream->master; 146362306a36Sopenharmony_ci } 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_ci return NULL; 146662306a36Sopenharmony_ci} 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_ci/* IRQ and event handlers */ 146962306a36Sopenharmony_cistatic int arm_smmu_handle_evt(struct arm_smmu_device *smmu, u64 *evt) 147062306a36Sopenharmony_ci{ 147162306a36Sopenharmony_ci int ret; 147262306a36Sopenharmony_ci u32 reason; 147362306a36Sopenharmony_ci u32 perm = 0; 147462306a36Sopenharmony_ci struct arm_smmu_master *master; 147562306a36Sopenharmony_ci bool ssid_valid = evt[0] & EVTQ_0_SSV; 147662306a36Sopenharmony_ci u32 sid = FIELD_GET(EVTQ_0_SID, evt[0]); 147762306a36Sopenharmony_ci struct iommu_fault_event fault_evt = { }; 147862306a36Sopenharmony_ci struct iommu_fault *flt = &fault_evt.fault; 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_ci switch (FIELD_GET(EVTQ_0_ID, evt[0])) { 148162306a36Sopenharmony_ci case EVT_ID_TRANSLATION_FAULT: 148262306a36Sopenharmony_ci reason = IOMMU_FAULT_REASON_PTE_FETCH; 148362306a36Sopenharmony_ci break; 148462306a36Sopenharmony_ci case EVT_ID_ADDR_SIZE_FAULT: 148562306a36Sopenharmony_ci reason = IOMMU_FAULT_REASON_OOR_ADDRESS; 148662306a36Sopenharmony_ci break; 148762306a36Sopenharmony_ci case EVT_ID_ACCESS_FAULT: 148862306a36Sopenharmony_ci reason = IOMMU_FAULT_REASON_ACCESS; 148962306a36Sopenharmony_ci break; 149062306a36Sopenharmony_ci case EVT_ID_PERMISSION_FAULT: 149162306a36Sopenharmony_ci reason = IOMMU_FAULT_REASON_PERMISSION; 149262306a36Sopenharmony_ci break; 149362306a36Sopenharmony_ci default: 149462306a36Sopenharmony_ci return -EOPNOTSUPP; 149562306a36Sopenharmony_ci } 149662306a36Sopenharmony_ci 149762306a36Sopenharmony_ci /* Stage-2 is always pinned at the moment */ 149862306a36Sopenharmony_ci if (evt[1] & EVTQ_1_S2) 149962306a36Sopenharmony_ci return -EFAULT; 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_ci if (evt[1] & EVTQ_1_RnW) 150262306a36Sopenharmony_ci perm |= IOMMU_FAULT_PERM_READ; 150362306a36Sopenharmony_ci else 150462306a36Sopenharmony_ci perm |= IOMMU_FAULT_PERM_WRITE; 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ci if (evt[1] & EVTQ_1_InD) 150762306a36Sopenharmony_ci perm |= IOMMU_FAULT_PERM_EXEC; 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_ci if (evt[1] & EVTQ_1_PnU) 151062306a36Sopenharmony_ci perm |= IOMMU_FAULT_PERM_PRIV; 151162306a36Sopenharmony_ci 151262306a36Sopenharmony_ci if (evt[1] & EVTQ_1_STALL) { 151362306a36Sopenharmony_ci flt->type = IOMMU_FAULT_PAGE_REQ; 151462306a36Sopenharmony_ci flt->prm = (struct iommu_fault_page_request) { 151562306a36Sopenharmony_ci .flags = IOMMU_FAULT_PAGE_REQUEST_LAST_PAGE, 151662306a36Sopenharmony_ci .grpid = FIELD_GET(EVTQ_1_STAG, evt[1]), 151762306a36Sopenharmony_ci .perm = perm, 151862306a36Sopenharmony_ci .addr = FIELD_GET(EVTQ_2_ADDR, evt[2]), 151962306a36Sopenharmony_ci }; 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_ci if (ssid_valid) { 152262306a36Sopenharmony_ci flt->prm.flags |= IOMMU_FAULT_PAGE_REQUEST_PASID_VALID; 152362306a36Sopenharmony_ci flt->prm.pasid = FIELD_GET(EVTQ_0_SSID, evt[0]); 152462306a36Sopenharmony_ci } 152562306a36Sopenharmony_ci } else { 152662306a36Sopenharmony_ci flt->type = IOMMU_FAULT_DMA_UNRECOV; 152762306a36Sopenharmony_ci flt->event = (struct iommu_fault_unrecoverable) { 152862306a36Sopenharmony_ci .reason = reason, 152962306a36Sopenharmony_ci .flags = IOMMU_FAULT_UNRECOV_ADDR_VALID, 153062306a36Sopenharmony_ci .perm = perm, 153162306a36Sopenharmony_ci .addr = FIELD_GET(EVTQ_2_ADDR, evt[2]), 153262306a36Sopenharmony_ci }; 153362306a36Sopenharmony_ci 153462306a36Sopenharmony_ci if (ssid_valid) { 153562306a36Sopenharmony_ci flt->event.flags |= IOMMU_FAULT_UNRECOV_PASID_VALID; 153662306a36Sopenharmony_ci flt->event.pasid = FIELD_GET(EVTQ_0_SSID, evt[0]); 153762306a36Sopenharmony_ci } 153862306a36Sopenharmony_ci } 153962306a36Sopenharmony_ci 154062306a36Sopenharmony_ci mutex_lock(&smmu->streams_mutex); 154162306a36Sopenharmony_ci master = arm_smmu_find_master(smmu, sid); 154262306a36Sopenharmony_ci if (!master) { 154362306a36Sopenharmony_ci ret = -EINVAL; 154462306a36Sopenharmony_ci goto out_unlock; 154562306a36Sopenharmony_ci } 154662306a36Sopenharmony_ci 154762306a36Sopenharmony_ci ret = iommu_report_device_fault(master->dev, &fault_evt); 154862306a36Sopenharmony_ci if (ret && flt->type == IOMMU_FAULT_PAGE_REQ) { 154962306a36Sopenharmony_ci /* Nobody cared, abort the access */ 155062306a36Sopenharmony_ci struct iommu_page_response resp = { 155162306a36Sopenharmony_ci .pasid = flt->prm.pasid, 155262306a36Sopenharmony_ci .grpid = flt->prm.grpid, 155362306a36Sopenharmony_ci .code = IOMMU_PAGE_RESP_FAILURE, 155462306a36Sopenharmony_ci }; 155562306a36Sopenharmony_ci arm_smmu_page_response(master->dev, &fault_evt, &resp); 155662306a36Sopenharmony_ci } 155762306a36Sopenharmony_ci 155862306a36Sopenharmony_ciout_unlock: 155962306a36Sopenharmony_ci mutex_unlock(&smmu->streams_mutex); 156062306a36Sopenharmony_ci return ret; 156162306a36Sopenharmony_ci} 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_cistatic irqreturn_t arm_smmu_evtq_thread(int irq, void *dev) 156462306a36Sopenharmony_ci{ 156562306a36Sopenharmony_ci int i, ret; 156662306a36Sopenharmony_ci struct arm_smmu_device *smmu = dev; 156762306a36Sopenharmony_ci struct arm_smmu_queue *q = &smmu->evtq.q; 156862306a36Sopenharmony_ci struct arm_smmu_ll_queue *llq = &q->llq; 156962306a36Sopenharmony_ci static DEFINE_RATELIMIT_STATE(rs, DEFAULT_RATELIMIT_INTERVAL, 157062306a36Sopenharmony_ci DEFAULT_RATELIMIT_BURST); 157162306a36Sopenharmony_ci u64 evt[EVTQ_ENT_DWORDS]; 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_ci do { 157462306a36Sopenharmony_ci while (!queue_remove_raw(q, evt)) { 157562306a36Sopenharmony_ci u8 id = FIELD_GET(EVTQ_0_ID, evt[0]); 157662306a36Sopenharmony_ci 157762306a36Sopenharmony_ci ret = arm_smmu_handle_evt(smmu, evt); 157862306a36Sopenharmony_ci if (!ret || !__ratelimit(&rs)) 157962306a36Sopenharmony_ci continue; 158062306a36Sopenharmony_ci 158162306a36Sopenharmony_ci dev_info(smmu->dev, "event 0x%02x received:\n", id); 158262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(evt); ++i) 158362306a36Sopenharmony_ci dev_info(smmu->dev, "\t0x%016llx\n", 158462306a36Sopenharmony_ci (unsigned long long)evt[i]); 158562306a36Sopenharmony_ci 158662306a36Sopenharmony_ci cond_resched(); 158762306a36Sopenharmony_ci } 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_ci /* 159062306a36Sopenharmony_ci * Not much we can do on overflow, so scream and pretend we're 159162306a36Sopenharmony_ci * trying harder. 159262306a36Sopenharmony_ci */ 159362306a36Sopenharmony_ci if (queue_sync_prod_in(q) == -EOVERFLOW) 159462306a36Sopenharmony_ci dev_err(smmu->dev, "EVTQ overflow detected -- events lost\n"); 159562306a36Sopenharmony_ci } while (!queue_empty(llq)); 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_ci /* Sync our overflow flag, as we believe we're up to speed */ 159862306a36Sopenharmony_ci queue_sync_cons_ovf(q); 159962306a36Sopenharmony_ci return IRQ_HANDLED; 160062306a36Sopenharmony_ci} 160162306a36Sopenharmony_ci 160262306a36Sopenharmony_cistatic void arm_smmu_handle_ppr(struct arm_smmu_device *smmu, u64 *evt) 160362306a36Sopenharmony_ci{ 160462306a36Sopenharmony_ci u32 sid, ssid; 160562306a36Sopenharmony_ci u16 grpid; 160662306a36Sopenharmony_ci bool ssv, last; 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_ci sid = FIELD_GET(PRIQ_0_SID, evt[0]); 160962306a36Sopenharmony_ci ssv = FIELD_GET(PRIQ_0_SSID_V, evt[0]); 161062306a36Sopenharmony_ci ssid = ssv ? FIELD_GET(PRIQ_0_SSID, evt[0]) : IOMMU_NO_PASID; 161162306a36Sopenharmony_ci last = FIELD_GET(PRIQ_0_PRG_LAST, evt[0]); 161262306a36Sopenharmony_ci grpid = FIELD_GET(PRIQ_1_PRG_IDX, evt[1]); 161362306a36Sopenharmony_ci 161462306a36Sopenharmony_ci dev_info(smmu->dev, "unexpected PRI request received:\n"); 161562306a36Sopenharmony_ci dev_info(smmu->dev, 161662306a36Sopenharmony_ci "\tsid 0x%08x.0x%05x: [%u%s] %sprivileged %s%s%s access at iova 0x%016llx\n", 161762306a36Sopenharmony_ci sid, ssid, grpid, last ? "L" : "", 161862306a36Sopenharmony_ci evt[0] & PRIQ_0_PERM_PRIV ? "" : "un", 161962306a36Sopenharmony_ci evt[0] & PRIQ_0_PERM_READ ? "R" : "", 162062306a36Sopenharmony_ci evt[0] & PRIQ_0_PERM_WRITE ? "W" : "", 162162306a36Sopenharmony_ci evt[0] & PRIQ_0_PERM_EXEC ? "X" : "", 162262306a36Sopenharmony_ci evt[1] & PRIQ_1_ADDR_MASK); 162362306a36Sopenharmony_ci 162462306a36Sopenharmony_ci if (last) { 162562306a36Sopenharmony_ci struct arm_smmu_cmdq_ent cmd = { 162662306a36Sopenharmony_ci .opcode = CMDQ_OP_PRI_RESP, 162762306a36Sopenharmony_ci .substream_valid = ssv, 162862306a36Sopenharmony_ci .pri = { 162962306a36Sopenharmony_ci .sid = sid, 163062306a36Sopenharmony_ci .ssid = ssid, 163162306a36Sopenharmony_ci .grpid = grpid, 163262306a36Sopenharmony_ci .resp = PRI_RESP_DENY, 163362306a36Sopenharmony_ci }, 163462306a36Sopenharmony_ci }; 163562306a36Sopenharmony_ci 163662306a36Sopenharmony_ci arm_smmu_cmdq_issue_cmd(smmu, &cmd); 163762306a36Sopenharmony_ci } 163862306a36Sopenharmony_ci} 163962306a36Sopenharmony_ci 164062306a36Sopenharmony_cistatic irqreturn_t arm_smmu_priq_thread(int irq, void *dev) 164162306a36Sopenharmony_ci{ 164262306a36Sopenharmony_ci struct arm_smmu_device *smmu = dev; 164362306a36Sopenharmony_ci struct arm_smmu_queue *q = &smmu->priq.q; 164462306a36Sopenharmony_ci struct arm_smmu_ll_queue *llq = &q->llq; 164562306a36Sopenharmony_ci u64 evt[PRIQ_ENT_DWORDS]; 164662306a36Sopenharmony_ci 164762306a36Sopenharmony_ci do { 164862306a36Sopenharmony_ci while (!queue_remove_raw(q, evt)) 164962306a36Sopenharmony_ci arm_smmu_handle_ppr(smmu, evt); 165062306a36Sopenharmony_ci 165162306a36Sopenharmony_ci if (queue_sync_prod_in(q) == -EOVERFLOW) 165262306a36Sopenharmony_ci dev_err(smmu->dev, "PRIQ overflow detected -- requests lost\n"); 165362306a36Sopenharmony_ci } while (!queue_empty(llq)); 165462306a36Sopenharmony_ci 165562306a36Sopenharmony_ci /* Sync our overflow flag, as we believe we're up to speed */ 165662306a36Sopenharmony_ci queue_sync_cons_ovf(q); 165762306a36Sopenharmony_ci return IRQ_HANDLED; 165862306a36Sopenharmony_ci} 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_cistatic int arm_smmu_device_disable(struct arm_smmu_device *smmu); 166162306a36Sopenharmony_ci 166262306a36Sopenharmony_cistatic irqreturn_t arm_smmu_gerror_handler(int irq, void *dev) 166362306a36Sopenharmony_ci{ 166462306a36Sopenharmony_ci u32 gerror, gerrorn, active; 166562306a36Sopenharmony_ci struct arm_smmu_device *smmu = dev; 166662306a36Sopenharmony_ci 166762306a36Sopenharmony_ci gerror = readl_relaxed(smmu->base + ARM_SMMU_GERROR); 166862306a36Sopenharmony_ci gerrorn = readl_relaxed(smmu->base + ARM_SMMU_GERRORN); 166962306a36Sopenharmony_ci 167062306a36Sopenharmony_ci active = gerror ^ gerrorn; 167162306a36Sopenharmony_ci if (!(active & GERROR_ERR_MASK)) 167262306a36Sopenharmony_ci return IRQ_NONE; /* No errors pending */ 167362306a36Sopenharmony_ci 167462306a36Sopenharmony_ci dev_warn(smmu->dev, 167562306a36Sopenharmony_ci "unexpected global error reported (0x%08x), this could be serious\n", 167662306a36Sopenharmony_ci active); 167762306a36Sopenharmony_ci 167862306a36Sopenharmony_ci if (active & GERROR_SFM_ERR) { 167962306a36Sopenharmony_ci dev_err(smmu->dev, "device has entered Service Failure Mode!\n"); 168062306a36Sopenharmony_ci arm_smmu_device_disable(smmu); 168162306a36Sopenharmony_ci } 168262306a36Sopenharmony_ci 168362306a36Sopenharmony_ci if (active & GERROR_MSI_GERROR_ABT_ERR) 168462306a36Sopenharmony_ci dev_warn(smmu->dev, "GERROR MSI write aborted\n"); 168562306a36Sopenharmony_ci 168662306a36Sopenharmony_ci if (active & GERROR_MSI_PRIQ_ABT_ERR) 168762306a36Sopenharmony_ci dev_warn(smmu->dev, "PRIQ MSI write aborted\n"); 168862306a36Sopenharmony_ci 168962306a36Sopenharmony_ci if (active & GERROR_MSI_EVTQ_ABT_ERR) 169062306a36Sopenharmony_ci dev_warn(smmu->dev, "EVTQ MSI write aborted\n"); 169162306a36Sopenharmony_ci 169262306a36Sopenharmony_ci if (active & GERROR_MSI_CMDQ_ABT_ERR) 169362306a36Sopenharmony_ci dev_warn(smmu->dev, "CMDQ MSI write aborted\n"); 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_ci if (active & GERROR_PRIQ_ABT_ERR) 169662306a36Sopenharmony_ci dev_err(smmu->dev, "PRIQ write aborted -- events may have been lost\n"); 169762306a36Sopenharmony_ci 169862306a36Sopenharmony_ci if (active & GERROR_EVTQ_ABT_ERR) 169962306a36Sopenharmony_ci dev_err(smmu->dev, "EVTQ write aborted -- events may have been lost\n"); 170062306a36Sopenharmony_ci 170162306a36Sopenharmony_ci if (active & GERROR_CMDQ_ERR) 170262306a36Sopenharmony_ci arm_smmu_cmdq_skip_err(smmu); 170362306a36Sopenharmony_ci 170462306a36Sopenharmony_ci writel(gerror, smmu->base + ARM_SMMU_GERRORN); 170562306a36Sopenharmony_ci return IRQ_HANDLED; 170662306a36Sopenharmony_ci} 170762306a36Sopenharmony_ci 170862306a36Sopenharmony_cistatic irqreturn_t arm_smmu_combined_irq_thread(int irq, void *dev) 170962306a36Sopenharmony_ci{ 171062306a36Sopenharmony_ci struct arm_smmu_device *smmu = dev; 171162306a36Sopenharmony_ci 171262306a36Sopenharmony_ci arm_smmu_evtq_thread(irq, dev); 171362306a36Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_PRI) 171462306a36Sopenharmony_ci arm_smmu_priq_thread(irq, dev); 171562306a36Sopenharmony_ci 171662306a36Sopenharmony_ci return IRQ_HANDLED; 171762306a36Sopenharmony_ci} 171862306a36Sopenharmony_ci 171962306a36Sopenharmony_cistatic irqreturn_t arm_smmu_combined_irq_handler(int irq, void *dev) 172062306a36Sopenharmony_ci{ 172162306a36Sopenharmony_ci arm_smmu_gerror_handler(irq, dev); 172262306a36Sopenharmony_ci return IRQ_WAKE_THREAD; 172362306a36Sopenharmony_ci} 172462306a36Sopenharmony_ci 172562306a36Sopenharmony_cistatic void 172662306a36Sopenharmony_ciarm_smmu_atc_inv_to_cmd(int ssid, unsigned long iova, size_t size, 172762306a36Sopenharmony_ci struct arm_smmu_cmdq_ent *cmd) 172862306a36Sopenharmony_ci{ 172962306a36Sopenharmony_ci size_t log2_span; 173062306a36Sopenharmony_ci size_t span_mask; 173162306a36Sopenharmony_ci /* ATC invalidates are always on 4096-bytes pages */ 173262306a36Sopenharmony_ci size_t inval_grain_shift = 12; 173362306a36Sopenharmony_ci unsigned long page_start, page_end; 173462306a36Sopenharmony_ci 173562306a36Sopenharmony_ci /* 173662306a36Sopenharmony_ci * ATS and PASID: 173762306a36Sopenharmony_ci * 173862306a36Sopenharmony_ci * If substream_valid is clear, the PCIe TLP is sent without a PASID 173962306a36Sopenharmony_ci * prefix. In that case all ATC entries within the address range are 174062306a36Sopenharmony_ci * invalidated, including those that were requested with a PASID! There 174162306a36Sopenharmony_ci * is no way to invalidate only entries without PASID. 174262306a36Sopenharmony_ci * 174362306a36Sopenharmony_ci * When using STRTAB_STE_1_S1DSS_SSID0 (reserving CD 0 for non-PASID 174462306a36Sopenharmony_ci * traffic), translation requests without PASID create ATC entries 174562306a36Sopenharmony_ci * without PASID, which must be invalidated with substream_valid clear. 174662306a36Sopenharmony_ci * This has the unpleasant side-effect of invalidating all PASID-tagged 174762306a36Sopenharmony_ci * ATC entries within the address range. 174862306a36Sopenharmony_ci */ 174962306a36Sopenharmony_ci *cmd = (struct arm_smmu_cmdq_ent) { 175062306a36Sopenharmony_ci .opcode = CMDQ_OP_ATC_INV, 175162306a36Sopenharmony_ci .substream_valid = (ssid != IOMMU_NO_PASID), 175262306a36Sopenharmony_ci .atc.ssid = ssid, 175362306a36Sopenharmony_ci }; 175462306a36Sopenharmony_ci 175562306a36Sopenharmony_ci if (!size) { 175662306a36Sopenharmony_ci cmd->atc.size = ATC_INV_SIZE_ALL; 175762306a36Sopenharmony_ci return; 175862306a36Sopenharmony_ci } 175962306a36Sopenharmony_ci 176062306a36Sopenharmony_ci page_start = iova >> inval_grain_shift; 176162306a36Sopenharmony_ci page_end = (iova + size - 1) >> inval_grain_shift; 176262306a36Sopenharmony_ci 176362306a36Sopenharmony_ci /* 176462306a36Sopenharmony_ci * In an ATS Invalidate Request, the address must be aligned on the 176562306a36Sopenharmony_ci * range size, which must be a power of two number of page sizes. We 176662306a36Sopenharmony_ci * thus have to choose between grossly over-invalidating the region, or 176762306a36Sopenharmony_ci * splitting the invalidation into multiple commands. For simplicity 176862306a36Sopenharmony_ci * we'll go with the first solution, but should refine it in the future 176962306a36Sopenharmony_ci * if multiple commands are shown to be more efficient. 177062306a36Sopenharmony_ci * 177162306a36Sopenharmony_ci * Find the smallest power of two that covers the range. The most 177262306a36Sopenharmony_ci * significant differing bit between the start and end addresses, 177362306a36Sopenharmony_ci * fls(start ^ end), indicates the required span. For example: 177462306a36Sopenharmony_ci * 177562306a36Sopenharmony_ci * We want to invalidate pages [8; 11]. This is already the ideal range: 177662306a36Sopenharmony_ci * x = 0b1000 ^ 0b1011 = 0b11 177762306a36Sopenharmony_ci * span = 1 << fls(x) = 4 177862306a36Sopenharmony_ci * 177962306a36Sopenharmony_ci * To invalidate pages [7; 10], we need to invalidate [0; 15]: 178062306a36Sopenharmony_ci * x = 0b0111 ^ 0b1010 = 0b1101 178162306a36Sopenharmony_ci * span = 1 << fls(x) = 16 178262306a36Sopenharmony_ci */ 178362306a36Sopenharmony_ci log2_span = fls_long(page_start ^ page_end); 178462306a36Sopenharmony_ci span_mask = (1ULL << log2_span) - 1; 178562306a36Sopenharmony_ci 178662306a36Sopenharmony_ci page_start &= ~span_mask; 178762306a36Sopenharmony_ci 178862306a36Sopenharmony_ci cmd->atc.addr = page_start << inval_grain_shift; 178962306a36Sopenharmony_ci cmd->atc.size = log2_span; 179062306a36Sopenharmony_ci} 179162306a36Sopenharmony_ci 179262306a36Sopenharmony_cistatic int arm_smmu_atc_inv_master(struct arm_smmu_master *master) 179362306a36Sopenharmony_ci{ 179462306a36Sopenharmony_ci int i; 179562306a36Sopenharmony_ci struct arm_smmu_cmdq_ent cmd; 179662306a36Sopenharmony_ci struct arm_smmu_cmdq_batch cmds; 179762306a36Sopenharmony_ci 179862306a36Sopenharmony_ci arm_smmu_atc_inv_to_cmd(IOMMU_NO_PASID, 0, 0, &cmd); 179962306a36Sopenharmony_ci 180062306a36Sopenharmony_ci cmds.num = 0; 180162306a36Sopenharmony_ci for (i = 0; i < master->num_streams; i++) { 180262306a36Sopenharmony_ci cmd.atc.sid = master->streams[i].id; 180362306a36Sopenharmony_ci arm_smmu_cmdq_batch_add(master->smmu, &cmds, &cmd); 180462306a36Sopenharmony_ci } 180562306a36Sopenharmony_ci 180662306a36Sopenharmony_ci return arm_smmu_cmdq_batch_submit(master->smmu, &cmds); 180762306a36Sopenharmony_ci} 180862306a36Sopenharmony_ci 180962306a36Sopenharmony_ciint arm_smmu_atc_inv_domain(struct arm_smmu_domain *smmu_domain, int ssid, 181062306a36Sopenharmony_ci unsigned long iova, size_t size) 181162306a36Sopenharmony_ci{ 181262306a36Sopenharmony_ci int i; 181362306a36Sopenharmony_ci unsigned long flags; 181462306a36Sopenharmony_ci struct arm_smmu_cmdq_ent cmd; 181562306a36Sopenharmony_ci struct arm_smmu_master *master; 181662306a36Sopenharmony_ci struct arm_smmu_cmdq_batch cmds; 181762306a36Sopenharmony_ci 181862306a36Sopenharmony_ci if (!(smmu_domain->smmu->features & ARM_SMMU_FEAT_ATS)) 181962306a36Sopenharmony_ci return 0; 182062306a36Sopenharmony_ci 182162306a36Sopenharmony_ci /* 182262306a36Sopenharmony_ci * Ensure that we've completed prior invalidation of the main TLBs 182362306a36Sopenharmony_ci * before we read 'nr_ats_masters' in case of a concurrent call to 182462306a36Sopenharmony_ci * arm_smmu_enable_ats(): 182562306a36Sopenharmony_ci * 182662306a36Sopenharmony_ci * // unmap() // arm_smmu_enable_ats() 182762306a36Sopenharmony_ci * TLBI+SYNC atomic_inc(&nr_ats_masters); 182862306a36Sopenharmony_ci * smp_mb(); [...] 182962306a36Sopenharmony_ci * atomic_read(&nr_ats_masters); pci_enable_ats() // writel() 183062306a36Sopenharmony_ci * 183162306a36Sopenharmony_ci * Ensures that we always see the incremented 'nr_ats_masters' count if 183262306a36Sopenharmony_ci * ATS was enabled at the PCI device before completion of the TLBI. 183362306a36Sopenharmony_ci */ 183462306a36Sopenharmony_ci smp_mb(); 183562306a36Sopenharmony_ci if (!atomic_read(&smmu_domain->nr_ats_masters)) 183662306a36Sopenharmony_ci return 0; 183762306a36Sopenharmony_ci 183862306a36Sopenharmony_ci arm_smmu_atc_inv_to_cmd(ssid, iova, size, &cmd); 183962306a36Sopenharmony_ci 184062306a36Sopenharmony_ci cmds.num = 0; 184162306a36Sopenharmony_ci 184262306a36Sopenharmony_ci spin_lock_irqsave(&smmu_domain->devices_lock, flags); 184362306a36Sopenharmony_ci list_for_each_entry(master, &smmu_domain->devices, domain_head) { 184462306a36Sopenharmony_ci if (!master->ats_enabled) 184562306a36Sopenharmony_ci continue; 184662306a36Sopenharmony_ci 184762306a36Sopenharmony_ci for (i = 0; i < master->num_streams; i++) { 184862306a36Sopenharmony_ci cmd.atc.sid = master->streams[i].id; 184962306a36Sopenharmony_ci arm_smmu_cmdq_batch_add(smmu_domain->smmu, &cmds, &cmd); 185062306a36Sopenharmony_ci } 185162306a36Sopenharmony_ci } 185262306a36Sopenharmony_ci spin_unlock_irqrestore(&smmu_domain->devices_lock, flags); 185362306a36Sopenharmony_ci 185462306a36Sopenharmony_ci return arm_smmu_cmdq_batch_submit(smmu_domain->smmu, &cmds); 185562306a36Sopenharmony_ci} 185662306a36Sopenharmony_ci 185762306a36Sopenharmony_ci/* IO_PGTABLE API */ 185862306a36Sopenharmony_cistatic void arm_smmu_tlb_inv_context(void *cookie) 185962306a36Sopenharmony_ci{ 186062306a36Sopenharmony_ci struct arm_smmu_domain *smmu_domain = cookie; 186162306a36Sopenharmony_ci struct arm_smmu_device *smmu = smmu_domain->smmu; 186262306a36Sopenharmony_ci struct arm_smmu_cmdq_ent cmd; 186362306a36Sopenharmony_ci 186462306a36Sopenharmony_ci /* 186562306a36Sopenharmony_ci * NOTE: when io-pgtable is in non-strict mode, we may get here with 186662306a36Sopenharmony_ci * PTEs previously cleared by unmaps on the current CPU not yet visible 186762306a36Sopenharmony_ci * to the SMMU. We are relying on the dma_wmb() implicit during cmd 186862306a36Sopenharmony_ci * insertion to guarantee those are observed before the TLBI. Do be 186962306a36Sopenharmony_ci * careful, 007. 187062306a36Sopenharmony_ci */ 187162306a36Sopenharmony_ci if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) { 187262306a36Sopenharmony_ci arm_smmu_tlb_inv_asid(smmu, smmu_domain->s1_cfg.cd.asid); 187362306a36Sopenharmony_ci } else { 187462306a36Sopenharmony_ci cmd.opcode = CMDQ_OP_TLBI_S12_VMALL; 187562306a36Sopenharmony_ci cmd.tlbi.vmid = smmu_domain->s2_cfg.vmid; 187662306a36Sopenharmony_ci arm_smmu_cmdq_issue_cmd_with_sync(smmu, &cmd); 187762306a36Sopenharmony_ci } 187862306a36Sopenharmony_ci arm_smmu_atc_inv_domain(smmu_domain, IOMMU_NO_PASID, 0, 0); 187962306a36Sopenharmony_ci} 188062306a36Sopenharmony_ci 188162306a36Sopenharmony_cistatic void __arm_smmu_tlb_inv_range(struct arm_smmu_cmdq_ent *cmd, 188262306a36Sopenharmony_ci unsigned long iova, size_t size, 188362306a36Sopenharmony_ci size_t granule, 188462306a36Sopenharmony_ci struct arm_smmu_domain *smmu_domain) 188562306a36Sopenharmony_ci{ 188662306a36Sopenharmony_ci struct arm_smmu_device *smmu = smmu_domain->smmu; 188762306a36Sopenharmony_ci unsigned long end = iova + size, num_pages = 0, tg = 0; 188862306a36Sopenharmony_ci size_t inv_range = granule; 188962306a36Sopenharmony_ci struct arm_smmu_cmdq_batch cmds; 189062306a36Sopenharmony_ci 189162306a36Sopenharmony_ci if (!size) 189262306a36Sopenharmony_ci return; 189362306a36Sopenharmony_ci 189462306a36Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_RANGE_INV) { 189562306a36Sopenharmony_ci /* Get the leaf page size */ 189662306a36Sopenharmony_ci tg = __ffs(smmu_domain->domain.pgsize_bitmap); 189762306a36Sopenharmony_ci 189862306a36Sopenharmony_ci num_pages = size >> tg; 189962306a36Sopenharmony_ci 190062306a36Sopenharmony_ci /* Convert page size of 12,14,16 (log2) to 1,2,3 */ 190162306a36Sopenharmony_ci cmd->tlbi.tg = (tg - 10) / 2; 190262306a36Sopenharmony_ci 190362306a36Sopenharmony_ci /* 190462306a36Sopenharmony_ci * Determine what level the granule is at. For non-leaf, both 190562306a36Sopenharmony_ci * io-pgtable and SVA pass a nominal last-level granule because 190662306a36Sopenharmony_ci * they don't know what level(s) actually apply, so ignore that 190762306a36Sopenharmony_ci * and leave TTL=0. However for various errata reasons we still 190862306a36Sopenharmony_ci * want to use a range command, so avoid the SVA corner case 190962306a36Sopenharmony_ci * where both scale and num could be 0 as well. 191062306a36Sopenharmony_ci */ 191162306a36Sopenharmony_ci if (cmd->tlbi.leaf) 191262306a36Sopenharmony_ci cmd->tlbi.ttl = 4 - ((ilog2(granule) - 3) / (tg - 3)); 191362306a36Sopenharmony_ci else if ((num_pages & CMDQ_TLBI_RANGE_NUM_MAX) == 1) 191462306a36Sopenharmony_ci num_pages++; 191562306a36Sopenharmony_ci } 191662306a36Sopenharmony_ci 191762306a36Sopenharmony_ci cmds.num = 0; 191862306a36Sopenharmony_ci 191962306a36Sopenharmony_ci while (iova < end) { 192062306a36Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_RANGE_INV) { 192162306a36Sopenharmony_ci /* 192262306a36Sopenharmony_ci * On each iteration of the loop, the range is 5 bits 192362306a36Sopenharmony_ci * worth of the aligned size remaining. 192462306a36Sopenharmony_ci * The range in pages is: 192562306a36Sopenharmony_ci * 192662306a36Sopenharmony_ci * range = (num_pages & (0x1f << __ffs(num_pages))) 192762306a36Sopenharmony_ci */ 192862306a36Sopenharmony_ci unsigned long scale, num; 192962306a36Sopenharmony_ci 193062306a36Sopenharmony_ci /* Determine the power of 2 multiple number of pages */ 193162306a36Sopenharmony_ci scale = __ffs(num_pages); 193262306a36Sopenharmony_ci cmd->tlbi.scale = scale; 193362306a36Sopenharmony_ci 193462306a36Sopenharmony_ci /* Determine how many chunks of 2^scale size we have */ 193562306a36Sopenharmony_ci num = (num_pages >> scale) & CMDQ_TLBI_RANGE_NUM_MAX; 193662306a36Sopenharmony_ci cmd->tlbi.num = num - 1; 193762306a36Sopenharmony_ci 193862306a36Sopenharmony_ci /* range is num * 2^scale * pgsize */ 193962306a36Sopenharmony_ci inv_range = num << (scale + tg); 194062306a36Sopenharmony_ci 194162306a36Sopenharmony_ci /* Clear out the lower order bits for the next iteration */ 194262306a36Sopenharmony_ci num_pages -= num << scale; 194362306a36Sopenharmony_ci } 194462306a36Sopenharmony_ci 194562306a36Sopenharmony_ci cmd->tlbi.addr = iova; 194662306a36Sopenharmony_ci arm_smmu_cmdq_batch_add(smmu, &cmds, cmd); 194762306a36Sopenharmony_ci iova += inv_range; 194862306a36Sopenharmony_ci } 194962306a36Sopenharmony_ci arm_smmu_cmdq_batch_submit(smmu, &cmds); 195062306a36Sopenharmony_ci} 195162306a36Sopenharmony_ci 195262306a36Sopenharmony_cistatic void arm_smmu_tlb_inv_range_domain(unsigned long iova, size_t size, 195362306a36Sopenharmony_ci size_t granule, bool leaf, 195462306a36Sopenharmony_ci struct arm_smmu_domain *smmu_domain) 195562306a36Sopenharmony_ci{ 195662306a36Sopenharmony_ci struct arm_smmu_cmdq_ent cmd = { 195762306a36Sopenharmony_ci .tlbi = { 195862306a36Sopenharmony_ci .leaf = leaf, 195962306a36Sopenharmony_ci }, 196062306a36Sopenharmony_ci }; 196162306a36Sopenharmony_ci 196262306a36Sopenharmony_ci if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) { 196362306a36Sopenharmony_ci cmd.opcode = smmu_domain->smmu->features & ARM_SMMU_FEAT_E2H ? 196462306a36Sopenharmony_ci CMDQ_OP_TLBI_EL2_VA : CMDQ_OP_TLBI_NH_VA; 196562306a36Sopenharmony_ci cmd.tlbi.asid = smmu_domain->s1_cfg.cd.asid; 196662306a36Sopenharmony_ci } else { 196762306a36Sopenharmony_ci cmd.opcode = CMDQ_OP_TLBI_S2_IPA; 196862306a36Sopenharmony_ci cmd.tlbi.vmid = smmu_domain->s2_cfg.vmid; 196962306a36Sopenharmony_ci } 197062306a36Sopenharmony_ci __arm_smmu_tlb_inv_range(&cmd, iova, size, granule, smmu_domain); 197162306a36Sopenharmony_ci 197262306a36Sopenharmony_ci /* 197362306a36Sopenharmony_ci * Unfortunately, this can't be leaf-only since we may have 197462306a36Sopenharmony_ci * zapped an entire table. 197562306a36Sopenharmony_ci */ 197662306a36Sopenharmony_ci arm_smmu_atc_inv_domain(smmu_domain, IOMMU_NO_PASID, iova, size); 197762306a36Sopenharmony_ci} 197862306a36Sopenharmony_ci 197962306a36Sopenharmony_civoid arm_smmu_tlb_inv_range_asid(unsigned long iova, size_t size, int asid, 198062306a36Sopenharmony_ci size_t granule, bool leaf, 198162306a36Sopenharmony_ci struct arm_smmu_domain *smmu_domain) 198262306a36Sopenharmony_ci{ 198362306a36Sopenharmony_ci struct arm_smmu_cmdq_ent cmd = { 198462306a36Sopenharmony_ci .opcode = smmu_domain->smmu->features & ARM_SMMU_FEAT_E2H ? 198562306a36Sopenharmony_ci CMDQ_OP_TLBI_EL2_VA : CMDQ_OP_TLBI_NH_VA, 198662306a36Sopenharmony_ci .tlbi = { 198762306a36Sopenharmony_ci .asid = asid, 198862306a36Sopenharmony_ci .leaf = leaf, 198962306a36Sopenharmony_ci }, 199062306a36Sopenharmony_ci }; 199162306a36Sopenharmony_ci 199262306a36Sopenharmony_ci __arm_smmu_tlb_inv_range(&cmd, iova, size, granule, smmu_domain); 199362306a36Sopenharmony_ci} 199462306a36Sopenharmony_ci 199562306a36Sopenharmony_cistatic void arm_smmu_tlb_inv_page_nosync(struct iommu_iotlb_gather *gather, 199662306a36Sopenharmony_ci unsigned long iova, size_t granule, 199762306a36Sopenharmony_ci void *cookie) 199862306a36Sopenharmony_ci{ 199962306a36Sopenharmony_ci struct arm_smmu_domain *smmu_domain = cookie; 200062306a36Sopenharmony_ci struct iommu_domain *domain = &smmu_domain->domain; 200162306a36Sopenharmony_ci 200262306a36Sopenharmony_ci iommu_iotlb_gather_add_page(domain, gather, iova, granule); 200362306a36Sopenharmony_ci} 200462306a36Sopenharmony_ci 200562306a36Sopenharmony_cistatic void arm_smmu_tlb_inv_walk(unsigned long iova, size_t size, 200662306a36Sopenharmony_ci size_t granule, void *cookie) 200762306a36Sopenharmony_ci{ 200862306a36Sopenharmony_ci arm_smmu_tlb_inv_range_domain(iova, size, granule, false, cookie); 200962306a36Sopenharmony_ci} 201062306a36Sopenharmony_ci 201162306a36Sopenharmony_cistatic const struct iommu_flush_ops arm_smmu_flush_ops = { 201262306a36Sopenharmony_ci .tlb_flush_all = arm_smmu_tlb_inv_context, 201362306a36Sopenharmony_ci .tlb_flush_walk = arm_smmu_tlb_inv_walk, 201462306a36Sopenharmony_ci .tlb_add_page = arm_smmu_tlb_inv_page_nosync, 201562306a36Sopenharmony_ci}; 201662306a36Sopenharmony_ci 201762306a36Sopenharmony_ci/* IOMMU API */ 201862306a36Sopenharmony_cistatic bool arm_smmu_capable(struct device *dev, enum iommu_cap cap) 201962306a36Sopenharmony_ci{ 202062306a36Sopenharmony_ci struct arm_smmu_master *master = dev_iommu_priv_get(dev); 202162306a36Sopenharmony_ci 202262306a36Sopenharmony_ci switch (cap) { 202362306a36Sopenharmony_ci case IOMMU_CAP_CACHE_COHERENCY: 202462306a36Sopenharmony_ci /* Assume that a coherent TCU implies coherent TBUs */ 202562306a36Sopenharmony_ci return master->smmu->features & ARM_SMMU_FEAT_COHERENCY; 202662306a36Sopenharmony_ci case IOMMU_CAP_NOEXEC: 202762306a36Sopenharmony_ci case IOMMU_CAP_DEFERRED_FLUSH: 202862306a36Sopenharmony_ci return true; 202962306a36Sopenharmony_ci default: 203062306a36Sopenharmony_ci return false; 203162306a36Sopenharmony_ci } 203262306a36Sopenharmony_ci} 203362306a36Sopenharmony_ci 203462306a36Sopenharmony_cistatic struct iommu_domain *arm_smmu_domain_alloc(unsigned type) 203562306a36Sopenharmony_ci{ 203662306a36Sopenharmony_ci struct arm_smmu_domain *smmu_domain; 203762306a36Sopenharmony_ci 203862306a36Sopenharmony_ci if (type == IOMMU_DOMAIN_SVA) 203962306a36Sopenharmony_ci return arm_smmu_sva_domain_alloc(); 204062306a36Sopenharmony_ci 204162306a36Sopenharmony_ci if (type != IOMMU_DOMAIN_UNMANAGED && 204262306a36Sopenharmony_ci type != IOMMU_DOMAIN_DMA && 204362306a36Sopenharmony_ci type != IOMMU_DOMAIN_IDENTITY) 204462306a36Sopenharmony_ci return NULL; 204562306a36Sopenharmony_ci 204662306a36Sopenharmony_ci /* 204762306a36Sopenharmony_ci * Allocate the domain and initialise some of its data structures. 204862306a36Sopenharmony_ci * We can't really do anything meaningful until we've added a 204962306a36Sopenharmony_ci * master. 205062306a36Sopenharmony_ci */ 205162306a36Sopenharmony_ci smmu_domain = kzalloc(sizeof(*smmu_domain), GFP_KERNEL); 205262306a36Sopenharmony_ci if (!smmu_domain) 205362306a36Sopenharmony_ci return NULL; 205462306a36Sopenharmony_ci 205562306a36Sopenharmony_ci mutex_init(&smmu_domain->init_mutex); 205662306a36Sopenharmony_ci INIT_LIST_HEAD(&smmu_domain->devices); 205762306a36Sopenharmony_ci spin_lock_init(&smmu_domain->devices_lock); 205862306a36Sopenharmony_ci INIT_LIST_HEAD(&smmu_domain->mmu_notifiers); 205962306a36Sopenharmony_ci 206062306a36Sopenharmony_ci return &smmu_domain->domain; 206162306a36Sopenharmony_ci} 206262306a36Sopenharmony_ci 206362306a36Sopenharmony_cistatic void arm_smmu_domain_free(struct iommu_domain *domain) 206462306a36Sopenharmony_ci{ 206562306a36Sopenharmony_ci struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); 206662306a36Sopenharmony_ci struct arm_smmu_device *smmu = smmu_domain->smmu; 206762306a36Sopenharmony_ci 206862306a36Sopenharmony_ci free_io_pgtable_ops(smmu_domain->pgtbl_ops); 206962306a36Sopenharmony_ci 207062306a36Sopenharmony_ci /* Free the CD and ASID, if we allocated them */ 207162306a36Sopenharmony_ci if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) { 207262306a36Sopenharmony_ci struct arm_smmu_s1_cfg *cfg = &smmu_domain->s1_cfg; 207362306a36Sopenharmony_ci 207462306a36Sopenharmony_ci /* Prevent SVA from touching the CD while we're freeing it */ 207562306a36Sopenharmony_ci mutex_lock(&arm_smmu_asid_lock); 207662306a36Sopenharmony_ci if (cfg->cdcfg.cdtab) 207762306a36Sopenharmony_ci arm_smmu_free_cd_tables(smmu_domain); 207862306a36Sopenharmony_ci arm_smmu_free_asid(&cfg->cd); 207962306a36Sopenharmony_ci mutex_unlock(&arm_smmu_asid_lock); 208062306a36Sopenharmony_ci } else { 208162306a36Sopenharmony_ci struct arm_smmu_s2_cfg *cfg = &smmu_domain->s2_cfg; 208262306a36Sopenharmony_ci if (cfg->vmid) 208362306a36Sopenharmony_ci ida_free(&smmu->vmid_map, cfg->vmid); 208462306a36Sopenharmony_ci } 208562306a36Sopenharmony_ci 208662306a36Sopenharmony_ci kfree(smmu_domain); 208762306a36Sopenharmony_ci} 208862306a36Sopenharmony_ci 208962306a36Sopenharmony_cistatic int arm_smmu_domain_finalise_s1(struct arm_smmu_domain *smmu_domain, 209062306a36Sopenharmony_ci struct arm_smmu_master *master, 209162306a36Sopenharmony_ci struct io_pgtable_cfg *pgtbl_cfg) 209262306a36Sopenharmony_ci{ 209362306a36Sopenharmony_ci int ret; 209462306a36Sopenharmony_ci u32 asid; 209562306a36Sopenharmony_ci struct arm_smmu_device *smmu = smmu_domain->smmu; 209662306a36Sopenharmony_ci struct arm_smmu_s1_cfg *cfg = &smmu_domain->s1_cfg; 209762306a36Sopenharmony_ci typeof(&pgtbl_cfg->arm_lpae_s1_cfg.tcr) tcr = &pgtbl_cfg->arm_lpae_s1_cfg.tcr; 209862306a36Sopenharmony_ci 209962306a36Sopenharmony_ci refcount_set(&cfg->cd.refs, 1); 210062306a36Sopenharmony_ci 210162306a36Sopenharmony_ci /* Prevent SVA from modifying the ASID until it is written to the CD */ 210262306a36Sopenharmony_ci mutex_lock(&arm_smmu_asid_lock); 210362306a36Sopenharmony_ci ret = xa_alloc(&arm_smmu_asid_xa, &asid, &cfg->cd, 210462306a36Sopenharmony_ci XA_LIMIT(1, (1 << smmu->asid_bits) - 1), GFP_KERNEL); 210562306a36Sopenharmony_ci if (ret) 210662306a36Sopenharmony_ci goto out_unlock; 210762306a36Sopenharmony_ci 210862306a36Sopenharmony_ci cfg->s1cdmax = master->ssid_bits; 210962306a36Sopenharmony_ci 211062306a36Sopenharmony_ci smmu_domain->stall_enabled = master->stall_enabled; 211162306a36Sopenharmony_ci 211262306a36Sopenharmony_ci ret = arm_smmu_alloc_cd_tables(smmu_domain); 211362306a36Sopenharmony_ci if (ret) 211462306a36Sopenharmony_ci goto out_free_asid; 211562306a36Sopenharmony_ci 211662306a36Sopenharmony_ci cfg->cd.asid = (u16)asid; 211762306a36Sopenharmony_ci cfg->cd.ttbr = pgtbl_cfg->arm_lpae_s1_cfg.ttbr; 211862306a36Sopenharmony_ci cfg->cd.tcr = FIELD_PREP(CTXDESC_CD_0_TCR_T0SZ, tcr->tsz) | 211962306a36Sopenharmony_ci FIELD_PREP(CTXDESC_CD_0_TCR_TG0, tcr->tg) | 212062306a36Sopenharmony_ci FIELD_PREP(CTXDESC_CD_0_TCR_IRGN0, tcr->irgn) | 212162306a36Sopenharmony_ci FIELD_PREP(CTXDESC_CD_0_TCR_ORGN0, tcr->orgn) | 212262306a36Sopenharmony_ci FIELD_PREP(CTXDESC_CD_0_TCR_SH0, tcr->sh) | 212362306a36Sopenharmony_ci FIELD_PREP(CTXDESC_CD_0_TCR_IPS, tcr->ips) | 212462306a36Sopenharmony_ci CTXDESC_CD_0_TCR_EPD1 | CTXDESC_CD_0_AA64; 212562306a36Sopenharmony_ci cfg->cd.mair = pgtbl_cfg->arm_lpae_s1_cfg.mair; 212662306a36Sopenharmony_ci 212762306a36Sopenharmony_ci /* 212862306a36Sopenharmony_ci * Note that this will end up calling arm_smmu_sync_cd() before 212962306a36Sopenharmony_ci * the master has been added to the devices list for this domain. 213062306a36Sopenharmony_ci * This isn't an issue because the STE hasn't been installed yet. 213162306a36Sopenharmony_ci */ 213262306a36Sopenharmony_ci ret = arm_smmu_write_ctx_desc(smmu_domain, IOMMU_NO_PASID, &cfg->cd); 213362306a36Sopenharmony_ci if (ret) 213462306a36Sopenharmony_ci goto out_free_cd_tables; 213562306a36Sopenharmony_ci 213662306a36Sopenharmony_ci mutex_unlock(&arm_smmu_asid_lock); 213762306a36Sopenharmony_ci return 0; 213862306a36Sopenharmony_ci 213962306a36Sopenharmony_ciout_free_cd_tables: 214062306a36Sopenharmony_ci arm_smmu_free_cd_tables(smmu_domain); 214162306a36Sopenharmony_ciout_free_asid: 214262306a36Sopenharmony_ci arm_smmu_free_asid(&cfg->cd); 214362306a36Sopenharmony_ciout_unlock: 214462306a36Sopenharmony_ci mutex_unlock(&arm_smmu_asid_lock); 214562306a36Sopenharmony_ci return ret; 214662306a36Sopenharmony_ci} 214762306a36Sopenharmony_ci 214862306a36Sopenharmony_cistatic int arm_smmu_domain_finalise_s2(struct arm_smmu_domain *smmu_domain, 214962306a36Sopenharmony_ci struct arm_smmu_master *master, 215062306a36Sopenharmony_ci struct io_pgtable_cfg *pgtbl_cfg) 215162306a36Sopenharmony_ci{ 215262306a36Sopenharmony_ci int vmid; 215362306a36Sopenharmony_ci struct arm_smmu_device *smmu = smmu_domain->smmu; 215462306a36Sopenharmony_ci struct arm_smmu_s2_cfg *cfg = &smmu_domain->s2_cfg; 215562306a36Sopenharmony_ci typeof(&pgtbl_cfg->arm_lpae_s2_cfg.vtcr) vtcr; 215662306a36Sopenharmony_ci 215762306a36Sopenharmony_ci /* Reserve VMID 0 for stage-2 bypass STEs */ 215862306a36Sopenharmony_ci vmid = ida_alloc_range(&smmu->vmid_map, 1, (1 << smmu->vmid_bits) - 1, 215962306a36Sopenharmony_ci GFP_KERNEL); 216062306a36Sopenharmony_ci if (vmid < 0) 216162306a36Sopenharmony_ci return vmid; 216262306a36Sopenharmony_ci 216362306a36Sopenharmony_ci vtcr = &pgtbl_cfg->arm_lpae_s2_cfg.vtcr; 216462306a36Sopenharmony_ci cfg->vmid = (u16)vmid; 216562306a36Sopenharmony_ci cfg->vttbr = pgtbl_cfg->arm_lpae_s2_cfg.vttbr; 216662306a36Sopenharmony_ci cfg->vtcr = FIELD_PREP(STRTAB_STE_2_VTCR_S2T0SZ, vtcr->tsz) | 216762306a36Sopenharmony_ci FIELD_PREP(STRTAB_STE_2_VTCR_S2SL0, vtcr->sl) | 216862306a36Sopenharmony_ci FIELD_PREP(STRTAB_STE_2_VTCR_S2IR0, vtcr->irgn) | 216962306a36Sopenharmony_ci FIELD_PREP(STRTAB_STE_2_VTCR_S2OR0, vtcr->orgn) | 217062306a36Sopenharmony_ci FIELD_PREP(STRTAB_STE_2_VTCR_S2SH0, vtcr->sh) | 217162306a36Sopenharmony_ci FIELD_PREP(STRTAB_STE_2_VTCR_S2TG, vtcr->tg) | 217262306a36Sopenharmony_ci FIELD_PREP(STRTAB_STE_2_VTCR_S2PS, vtcr->ps); 217362306a36Sopenharmony_ci return 0; 217462306a36Sopenharmony_ci} 217562306a36Sopenharmony_ci 217662306a36Sopenharmony_cistatic int arm_smmu_domain_finalise(struct iommu_domain *domain, 217762306a36Sopenharmony_ci struct arm_smmu_master *master) 217862306a36Sopenharmony_ci{ 217962306a36Sopenharmony_ci int ret; 218062306a36Sopenharmony_ci unsigned long ias, oas; 218162306a36Sopenharmony_ci enum io_pgtable_fmt fmt; 218262306a36Sopenharmony_ci struct io_pgtable_cfg pgtbl_cfg; 218362306a36Sopenharmony_ci struct io_pgtable_ops *pgtbl_ops; 218462306a36Sopenharmony_ci int (*finalise_stage_fn)(struct arm_smmu_domain *, 218562306a36Sopenharmony_ci struct arm_smmu_master *, 218662306a36Sopenharmony_ci struct io_pgtable_cfg *); 218762306a36Sopenharmony_ci struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); 218862306a36Sopenharmony_ci struct arm_smmu_device *smmu = smmu_domain->smmu; 218962306a36Sopenharmony_ci 219062306a36Sopenharmony_ci if (domain->type == IOMMU_DOMAIN_IDENTITY) { 219162306a36Sopenharmony_ci smmu_domain->stage = ARM_SMMU_DOMAIN_BYPASS; 219262306a36Sopenharmony_ci return 0; 219362306a36Sopenharmony_ci } 219462306a36Sopenharmony_ci 219562306a36Sopenharmony_ci /* Restrict the stage to what we can actually support */ 219662306a36Sopenharmony_ci if (!(smmu->features & ARM_SMMU_FEAT_TRANS_S1)) 219762306a36Sopenharmony_ci smmu_domain->stage = ARM_SMMU_DOMAIN_S2; 219862306a36Sopenharmony_ci if (!(smmu->features & ARM_SMMU_FEAT_TRANS_S2)) 219962306a36Sopenharmony_ci smmu_domain->stage = ARM_SMMU_DOMAIN_S1; 220062306a36Sopenharmony_ci 220162306a36Sopenharmony_ci switch (smmu_domain->stage) { 220262306a36Sopenharmony_ci case ARM_SMMU_DOMAIN_S1: 220362306a36Sopenharmony_ci ias = (smmu->features & ARM_SMMU_FEAT_VAX) ? 52 : 48; 220462306a36Sopenharmony_ci ias = min_t(unsigned long, ias, VA_BITS); 220562306a36Sopenharmony_ci oas = smmu->ias; 220662306a36Sopenharmony_ci fmt = ARM_64_LPAE_S1; 220762306a36Sopenharmony_ci finalise_stage_fn = arm_smmu_domain_finalise_s1; 220862306a36Sopenharmony_ci break; 220962306a36Sopenharmony_ci case ARM_SMMU_DOMAIN_NESTED: 221062306a36Sopenharmony_ci case ARM_SMMU_DOMAIN_S2: 221162306a36Sopenharmony_ci ias = smmu->ias; 221262306a36Sopenharmony_ci oas = smmu->oas; 221362306a36Sopenharmony_ci fmt = ARM_64_LPAE_S2; 221462306a36Sopenharmony_ci finalise_stage_fn = arm_smmu_domain_finalise_s2; 221562306a36Sopenharmony_ci break; 221662306a36Sopenharmony_ci default: 221762306a36Sopenharmony_ci return -EINVAL; 221862306a36Sopenharmony_ci } 221962306a36Sopenharmony_ci 222062306a36Sopenharmony_ci pgtbl_cfg = (struct io_pgtable_cfg) { 222162306a36Sopenharmony_ci .pgsize_bitmap = smmu->pgsize_bitmap, 222262306a36Sopenharmony_ci .ias = ias, 222362306a36Sopenharmony_ci .oas = oas, 222462306a36Sopenharmony_ci .coherent_walk = smmu->features & ARM_SMMU_FEAT_COHERENCY, 222562306a36Sopenharmony_ci .tlb = &arm_smmu_flush_ops, 222662306a36Sopenharmony_ci .iommu_dev = smmu->dev, 222762306a36Sopenharmony_ci }; 222862306a36Sopenharmony_ci 222962306a36Sopenharmony_ci pgtbl_ops = alloc_io_pgtable_ops(fmt, &pgtbl_cfg, smmu_domain); 223062306a36Sopenharmony_ci if (!pgtbl_ops) 223162306a36Sopenharmony_ci return -ENOMEM; 223262306a36Sopenharmony_ci 223362306a36Sopenharmony_ci domain->pgsize_bitmap = pgtbl_cfg.pgsize_bitmap; 223462306a36Sopenharmony_ci domain->geometry.aperture_end = (1UL << pgtbl_cfg.ias) - 1; 223562306a36Sopenharmony_ci domain->geometry.force_aperture = true; 223662306a36Sopenharmony_ci 223762306a36Sopenharmony_ci ret = finalise_stage_fn(smmu_domain, master, &pgtbl_cfg); 223862306a36Sopenharmony_ci if (ret < 0) { 223962306a36Sopenharmony_ci free_io_pgtable_ops(pgtbl_ops); 224062306a36Sopenharmony_ci return ret; 224162306a36Sopenharmony_ci } 224262306a36Sopenharmony_ci 224362306a36Sopenharmony_ci smmu_domain->pgtbl_ops = pgtbl_ops; 224462306a36Sopenharmony_ci return 0; 224562306a36Sopenharmony_ci} 224662306a36Sopenharmony_ci 224762306a36Sopenharmony_cistatic __le64 *arm_smmu_get_step_for_sid(struct arm_smmu_device *smmu, u32 sid) 224862306a36Sopenharmony_ci{ 224962306a36Sopenharmony_ci __le64 *step; 225062306a36Sopenharmony_ci struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg; 225162306a36Sopenharmony_ci 225262306a36Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB) { 225362306a36Sopenharmony_ci struct arm_smmu_strtab_l1_desc *l1_desc; 225462306a36Sopenharmony_ci int idx; 225562306a36Sopenharmony_ci 225662306a36Sopenharmony_ci /* Two-level walk */ 225762306a36Sopenharmony_ci idx = (sid >> STRTAB_SPLIT) * STRTAB_L1_DESC_DWORDS; 225862306a36Sopenharmony_ci l1_desc = &cfg->l1_desc[idx]; 225962306a36Sopenharmony_ci idx = (sid & ((1 << STRTAB_SPLIT) - 1)) * STRTAB_STE_DWORDS; 226062306a36Sopenharmony_ci step = &l1_desc->l2ptr[idx]; 226162306a36Sopenharmony_ci } else { 226262306a36Sopenharmony_ci /* Simple linear lookup */ 226362306a36Sopenharmony_ci step = &cfg->strtab[sid * STRTAB_STE_DWORDS]; 226462306a36Sopenharmony_ci } 226562306a36Sopenharmony_ci 226662306a36Sopenharmony_ci return step; 226762306a36Sopenharmony_ci} 226862306a36Sopenharmony_ci 226962306a36Sopenharmony_cistatic void arm_smmu_install_ste_for_dev(struct arm_smmu_master *master) 227062306a36Sopenharmony_ci{ 227162306a36Sopenharmony_ci int i, j; 227262306a36Sopenharmony_ci struct arm_smmu_device *smmu = master->smmu; 227362306a36Sopenharmony_ci 227462306a36Sopenharmony_ci for (i = 0; i < master->num_streams; ++i) { 227562306a36Sopenharmony_ci u32 sid = master->streams[i].id; 227662306a36Sopenharmony_ci __le64 *step = arm_smmu_get_step_for_sid(smmu, sid); 227762306a36Sopenharmony_ci 227862306a36Sopenharmony_ci /* Bridged PCI devices may end up with duplicated IDs */ 227962306a36Sopenharmony_ci for (j = 0; j < i; j++) 228062306a36Sopenharmony_ci if (master->streams[j].id == sid) 228162306a36Sopenharmony_ci break; 228262306a36Sopenharmony_ci if (j < i) 228362306a36Sopenharmony_ci continue; 228462306a36Sopenharmony_ci 228562306a36Sopenharmony_ci arm_smmu_write_strtab_ent(master, sid, step); 228662306a36Sopenharmony_ci } 228762306a36Sopenharmony_ci} 228862306a36Sopenharmony_ci 228962306a36Sopenharmony_cistatic bool arm_smmu_ats_supported(struct arm_smmu_master *master) 229062306a36Sopenharmony_ci{ 229162306a36Sopenharmony_ci struct device *dev = master->dev; 229262306a36Sopenharmony_ci struct arm_smmu_device *smmu = master->smmu; 229362306a36Sopenharmony_ci struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); 229462306a36Sopenharmony_ci 229562306a36Sopenharmony_ci if (!(smmu->features & ARM_SMMU_FEAT_ATS)) 229662306a36Sopenharmony_ci return false; 229762306a36Sopenharmony_ci 229862306a36Sopenharmony_ci if (!(fwspec->flags & IOMMU_FWSPEC_PCI_RC_ATS)) 229962306a36Sopenharmony_ci return false; 230062306a36Sopenharmony_ci 230162306a36Sopenharmony_ci return dev_is_pci(dev) && pci_ats_supported(to_pci_dev(dev)); 230262306a36Sopenharmony_ci} 230362306a36Sopenharmony_ci 230462306a36Sopenharmony_cistatic void arm_smmu_enable_ats(struct arm_smmu_master *master) 230562306a36Sopenharmony_ci{ 230662306a36Sopenharmony_ci size_t stu; 230762306a36Sopenharmony_ci struct pci_dev *pdev; 230862306a36Sopenharmony_ci struct arm_smmu_device *smmu = master->smmu; 230962306a36Sopenharmony_ci struct arm_smmu_domain *smmu_domain = master->domain; 231062306a36Sopenharmony_ci 231162306a36Sopenharmony_ci /* Don't enable ATS at the endpoint if it's not enabled in the STE */ 231262306a36Sopenharmony_ci if (!master->ats_enabled) 231362306a36Sopenharmony_ci return; 231462306a36Sopenharmony_ci 231562306a36Sopenharmony_ci /* Smallest Translation Unit: log2 of the smallest supported granule */ 231662306a36Sopenharmony_ci stu = __ffs(smmu->pgsize_bitmap); 231762306a36Sopenharmony_ci pdev = to_pci_dev(master->dev); 231862306a36Sopenharmony_ci 231962306a36Sopenharmony_ci atomic_inc(&smmu_domain->nr_ats_masters); 232062306a36Sopenharmony_ci arm_smmu_atc_inv_domain(smmu_domain, IOMMU_NO_PASID, 0, 0); 232162306a36Sopenharmony_ci if (pci_enable_ats(pdev, stu)) 232262306a36Sopenharmony_ci dev_err(master->dev, "Failed to enable ATS (STU %zu)\n", stu); 232362306a36Sopenharmony_ci} 232462306a36Sopenharmony_ci 232562306a36Sopenharmony_cistatic void arm_smmu_disable_ats(struct arm_smmu_master *master) 232662306a36Sopenharmony_ci{ 232762306a36Sopenharmony_ci struct arm_smmu_domain *smmu_domain = master->domain; 232862306a36Sopenharmony_ci 232962306a36Sopenharmony_ci if (!master->ats_enabled) 233062306a36Sopenharmony_ci return; 233162306a36Sopenharmony_ci 233262306a36Sopenharmony_ci pci_disable_ats(to_pci_dev(master->dev)); 233362306a36Sopenharmony_ci /* 233462306a36Sopenharmony_ci * Ensure ATS is disabled at the endpoint before we issue the 233562306a36Sopenharmony_ci * ATC invalidation via the SMMU. 233662306a36Sopenharmony_ci */ 233762306a36Sopenharmony_ci wmb(); 233862306a36Sopenharmony_ci arm_smmu_atc_inv_master(master); 233962306a36Sopenharmony_ci atomic_dec(&smmu_domain->nr_ats_masters); 234062306a36Sopenharmony_ci} 234162306a36Sopenharmony_ci 234262306a36Sopenharmony_cistatic int arm_smmu_enable_pasid(struct arm_smmu_master *master) 234362306a36Sopenharmony_ci{ 234462306a36Sopenharmony_ci int ret; 234562306a36Sopenharmony_ci int features; 234662306a36Sopenharmony_ci int num_pasids; 234762306a36Sopenharmony_ci struct pci_dev *pdev; 234862306a36Sopenharmony_ci 234962306a36Sopenharmony_ci if (!dev_is_pci(master->dev)) 235062306a36Sopenharmony_ci return -ENODEV; 235162306a36Sopenharmony_ci 235262306a36Sopenharmony_ci pdev = to_pci_dev(master->dev); 235362306a36Sopenharmony_ci 235462306a36Sopenharmony_ci features = pci_pasid_features(pdev); 235562306a36Sopenharmony_ci if (features < 0) 235662306a36Sopenharmony_ci return features; 235762306a36Sopenharmony_ci 235862306a36Sopenharmony_ci num_pasids = pci_max_pasids(pdev); 235962306a36Sopenharmony_ci if (num_pasids <= 0) 236062306a36Sopenharmony_ci return num_pasids; 236162306a36Sopenharmony_ci 236262306a36Sopenharmony_ci ret = pci_enable_pasid(pdev, features); 236362306a36Sopenharmony_ci if (ret) { 236462306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to enable PASID\n"); 236562306a36Sopenharmony_ci return ret; 236662306a36Sopenharmony_ci } 236762306a36Sopenharmony_ci 236862306a36Sopenharmony_ci master->ssid_bits = min_t(u8, ilog2(num_pasids), 236962306a36Sopenharmony_ci master->smmu->ssid_bits); 237062306a36Sopenharmony_ci return 0; 237162306a36Sopenharmony_ci} 237262306a36Sopenharmony_ci 237362306a36Sopenharmony_cistatic void arm_smmu_disable_pasid(struct arm_smmu_master *master) 237462306a36Sopenharmony_ci{ 237562306a36Sopenharmony_ci struct pci_dev *pdev; 237662306a36Sopenharmony_ci 237762306a36Sopenharmony_ci if (!dev_is_pci(master->dev)) 237862306a36Sopenharmony_ci return; 237962306a36Sopenharmony_ci 238062306a36Sopenharmony_ci pdev = to_pci_dev(master->dev); 238162306a36Sopenharmony_ci 238262306a36Sopenharmony_ci if (!pdev->pasid_enabled) 238362306a36Sopenharmony_ci return; 238462306a36Sopenharmony_ci 238562306a36Sopenharmony_ci master->ssid_bits = 0; 238662306a36Sopenharmony_ci pci_disable_pasid(pdev); 238762306a36Sopenharmony_ci} 238862306a36Sopenharmony_ci 238962306a36Sopenharmony_cistatic void arm_smmu_detach_dev(struct arm_smmu_master *master) 239062306a36Sopenharmony_ci{ 239162306a36Sopenharmony_ci unsigned long flags; 239262306a36Sopenharmony_ci struct arm_smmu_domain *smmu_domain = master->domain; 239362306a36Sopenharmony_ci 239462306a36Sopenharmony_ci if (!smmu_domain) 239562306a36Sopenharmony_ci return; 239662306a36Sopenharmony_ci 239762306a36Sopenharmony_ci arm_smmu_disable_ats(master); 239862306a36Sopenharmony_ci 239962306a36Sopenharmony_ci spin_lock_irqsave(&smmu_domain->devices_lock, flags); 240062306a36Sopenharmony_ci list_del(&master->domain_head); 240162306a36Sopenharmony_ci spin_unlock_irqrestore(&smmu_domain->devices_lock, flags); 240262306a36Sopenharmony_ci 240362306a36Sopenharmony_ci master->domain = NULL; 240462306a36Sopenharmony_ci master->ats_enabled = false; 240562306a36Sopenharmony_ci arm_smmu_install_ste_for_dev(master); 240662306a36Sopenharmony_ci} 240762306a36Sopenharmony_ci 240862306a36Sopenharmony_cistatic int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) 240962306a36Sopenharmony_ci{ 241062306a36Sopenharmony_ci int ret = 0; 241162306a36Sopenharmony_ci unsigned long flags; 241262306a36Sopenharmony_ci struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); 241362306a36Sopenharmony_ci struct arm_smmu_device *smmu; 241462306a36Sopenharmony_ci struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); 241562306a36Sopenharmony_ci struct arm_smmu_master *master; 241662306a36Sopenharmony_ci 241762306a36Sopenharmony_ci if (!fwspec) 241862306a36Sopenharmony_ci return -ENOENT; 241962306a36Sopenharmony_ci 242062306a36Sopenharmony_ci master = dev_iommu_priv_get(dev); 242162306a36Sopenharmony_ci smmu = master->smmu; 242262306a36Sopenharmony_ci 242362306a36Sopenharmony_ci /* 242462306a36Sopenharmony_ci * Checking that SVA is disabled ensures that this device isn't bound to 242562306a36Sopenharmony_ci * any mm, and can be safely detached from its old domain. Bonds cannot 242662306a36Sopenharmony_ci * be removed concurrently since we're holding the group mutex. 242762306a36Sopenharmony_ci */ 242862306a36Sopenharmony_ci if (arm_smmu_master_sva_enabled(master)) { 242962306a36Sopenharmony_ci dev_err(dev, "cannot attach - SVA enabled\n"); 243062306a36Sopenharmony_ci return -EBUSY; 243162306a36Sopenharmony_ci } 243262306a36Sopenharmony_ci 243362306a36Sopenharmony_ci arm_smmu_detach_dev(master); 243462306a36Sopenharmony_ci 243562306a36Sopenharmony_ci mutex_lock(&smmu_domain->init_mutex); 243662306a36Sopenharmony_ci 243762306a36Sopenharmony_ci if (!smmu_domain->smmu) { 243862306a36Sopenharmony_ci smmu_domain->smmu = smmu; 243962306a36Sopenharmony_ci ret = arm_smmu_domain_finalise(domain, master); 244062306a36Sopenharmony_ci if (ret) { 244162306a36Sopenharmony_ci smmu_domain->smmu = NULL; 244262306a36Sopenharmony_ci goto out_unlock; 244362306a36Sopenharmony_ci } 244462306a36Sopenharmony_ci } else if (smmu_domain->smmu != smmu) { 244562306a36Sopenharmony_ci ret = -EINVAL; 244662306a36Sopenharmony_ci goto out_unlock; 244762306a36Sopenharmony_ci } else if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1 && 244862306a36Sopenharmony_ci master->ssid_bits != smmu_domain->s1_cfg.s1cdmax) { 244962306a36Sopenharmony_ci ret = -EINVAL; 245062306a36Sopenharmony_ci goto out_unlock; 245162306a36Sopenharmony_ci } else if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1 && 245262306a36Sopenharmony_ci smmu_domain->stall_enabled != master->stall_enabled) { 245362306a36Sopenharmony_ci ret = -EINVAL; 245462306a36Sopenharmony_ci goto out_unlock; 245562306a36Sopenharmony_ci } 245662306a36Sopenharmony_ci 245762306a36Sopenharmony_ci master->domain = smmu_domain; 245862306a36Sopenharmony_ci 245962306a36Sopenharmony_ci /* 246062306a36Sopenharmony_ci * The SMMU does not support enabling ATS with bypass. When the STE is 246162306a36Sopenharmony_ci * in bypass (STE.Config[2:0] == 0b100), ATS Translation Requests and 246262306a36Sopenharmony_ci * Translated transactions are denied as though ATS is disabled for the 246362306a36Sopenharmony_ci * stream (STE.EATS == 0b00), causing F_BAD_ATS_TREQ and 246462306a36Sopenharmony_ci * F_TRANSL_FORBIDDEN events (IHI0070Ea 5.2 Stream Table Entry). 246562306a36Sopenharmony_ci */ 246662306a36Sopenharmony_ci if (smmu_domain->stage != ARM_SMMU_DOMAIN_BYPASS) 246762306a36Sopenharmony_ci master->ats_enabled = arm_smmu_ats_supported(master); 246862306a36Sopenharmony_ci 246962306a36Sopenharmony_ci arm_smmu_install_ste_for_dev(master); 247062306a36Sopenharmony_ci 247162306a36Sopenharmony_ci spin_lock_irqsave(&smmu_domain->devices_lock, flags); 247262306a36Sopenharmony_ci list_add(&master->domain_head, &smmu_domain->devices); 247362306a36Sopenharmony_ci spin_unlock_irqrestore(&smmu_domain->devices_lock, flags); 247462306a36Sopenharmony_ci 247562306a36Sopenharmony_ci arm_smmu_enable_ats(master); 247662306a36Sopenharmony_ci 247762306a36Sopenharmony_ciout_unlock: 247862306a36Sopenharmony_ci mutex_unlock(&smmu_domain->init_mutex); 247962306a36Sopenharmony_ci return ret; 248062306a36Sopenharmony_ci} 248162306a36Sopenharmony_ci 248262306a36Sopenharmony_cistatic int arm_smmu_map_pages(struct iommu_domain *domain, unsigned long iova, 248362306a36Sopenharmony_ci phys_addr_t paddr, size_t pgsize, size_t pgcount, 248462306a36Sopenharmony_ci int prot, gfp_t gfp, size_t *mapped) 248562306a36Sopenharmony_ci{ 248662306a36Sopenharmony_ci struct io_pgtable_ops *ops = to_smmu_domain(domain)->pgtbl_ops; 248762306a36Sopenharmony_ci 248862306a36Sopenharmony_ci if (!ops) 248962306a36Sopenharmony_ci return -ENODEV; 249062306a36Sopenharmony_ci 249162306a36Sopenharmony_ci return ops->map_pages(ops, iova, paddr, pgsize, pgcount, prot, gfp, mapped); 249262306a36Sopenharmony_ci} 249362306a36Sopenharmony_ci 249462306a36Sopenharmony_cistatic size_t arm_smmu_unmap_pages(struct iommu_domain *domain, unsigned long iova, 249562306a36Sopenharmony_ci size_t pgsize, size_t pgcount, 249662306a36Sopenharmony_ci struct iommu_iotlb_gather *gather) 249762306a36Sopenharmony_ci{ 249862306a36Sopenharmony_ci struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); 249962306a36Sopenharmony_ci struct io_pgtable_ops *ops = smmu_domain->pgtbl_ops; 250062306a36Sopenharmony_ci 250162306a36Sopenharmony_ci if (!ops) 250262306a36Sopenharmony_ci return 0; 250362306a36Sopenharmony_ci 250462306a36Sopenharmony_ci return ops->unmap_pages(ops, iova, pgsize, pgcount, gather); 250562306a36Sopenharmony_ci} 250662306a36Sopenharmony_ci 250762306a36Sopenharmony_cistatic void arm_smmu_flush_iotlb_all(struct iommu_domain *domain) 250862306a36Sopenharmony_ci{ 250962306a36Sopenharmony_ci struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); 251062306a36Sopenharmony_ci 251162306a36Sopenharmony_ci if (smmu_domain->smmu) 251262306a36Sopenharmony_ci arm_smmu_tlb_inv_context(smmu_domain); 251362306a36Sopenharmony_ci} 251462306a36Sopenharmony_ci 251562306a36Sopenharmony_cistatic void arm_smmu_iotlb_sync(struct iommu_domain *domain, 251662306a36Sopenharmony_ci struct iommu_iotlb_gather *gather) 251762306a36Sopenharmony_ci{ 251862306a36Sopenharmony_ci struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); 251962306a36Sopenharmony_ci 252062306a36Sopenharmony_ci if (!gather->pgsize) 252162306a36Sopenharmony_ci return; 252262306a36Sopenharmony_ci 252362306a36Sopenharmony_ci arm_smmu_tlb_inv_range_domain(gather->start, 252462306a36Sopenharmony_ci gather->end - gather->start + 1, 252562306a36Sopenharmony_ci gather->pgsize, true, smmu_domain); 252662306a36Sopenharmony_ci} 252762306a36Sopenharmony_ci 252862306a36Sopenharmony_cistatic phys_addr_t 252962306a36Sopenharmony_ciarm_smmu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova) 253062306a36Sopenharmony_ci{ 253162306a36Sopenharmony_ci struct io_pgtable_ops *ops = to_smmu_domain(domain)->pgtbl_ops; 253262306a36Sopenharmony_ci 253362306a36Sopenharmony_ci if (!ops) 253462306a36Sopenharmony_ci return 0; 253562306a36Sopenharmony_ci 253662306a36Sopenharmony_ci return ops->iova_to_phys(ops, iova); 253762306a36Sopenharmony_ci} 253862306a36Sopenharmony_ci 253962306a36Sopenharmony_cistatic struct platform_driver arm_smmu_driver; 254062306a36Sopenharmony_ci 254162306a36Sopenharmony_cistatic 254262306a36Sopenharmony_cistruct arm_smmu_device *arm_smmu_get_by_fwnode(struct fwnode_handle *fwnode) 254362306a36Sopenharmony_ci{ 254462306a36Sopenharmony_ci struct device *dev = driver_find_device_by_fwnode(&arm_smmu_driver.driver, 254562306a36Sopenharmony_ci fwnode); 254662306a36Sopenharmony_ci put_device(dev); 254762306a36Sopenharmony_ci return dev ? dev_get_drvdata(dev) : NULL; 254862306a36Sopenharmony_ci} 254962306a36Sopenharmony_ci 255062306a36Sopenharmony_cistatic bool arm_smmu_sid_in_range(struct arm_smmu_device *smmu, u32 sid) 255162306a36Sopenharmony_ci{ 255262306a36Sopenharmony_ci unsigned long limit = smmu->strtab_cfg.num_l1_ents; 255362306a36Sopenharmony_ci 255462306a36Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB) 255562306a36Sopenharmony_ci limit *= 1UL << STRTAB_SPLIT; 255662306a36Sopenharmony_ci 255762306a36Sopenharmony_ci return sid < limit; 255862306a36Sopenharmony_ci} 255962306a36Sopenharmony_ci 256062306a36Sopenharmony_cistatic int arm_smmu_init_sid_strtab(struct arm_smmu_device *smmu, u32 sid) 256162306a36Sopenharmony_ci{ 256262306a36Sopenharmony_ci /* Check the SIDs are in range of the SMMU and our stream table */ 256362306a36Sopenharmony_ci if (!arm_smmu_sid_in_range(smmu, sid)) 256462306a36Sopenharmony_ci return -ERANGE; 256562306a36Sopenharmony_ci 256662306a36Sopenharmony_ci /* Ensure l2 strtab is initialised */ 256762306a36Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB) 256862306a36Sopenharmony_ci return arm_smmu_init_l2_strtab(smmu, sid); 256962306a36Sopenharmony_ci 257062306a36Sopenharmony_ci return 0; 257162306a36Sopenharmony_ci} 257262306a36Sopenharmony_ci 257362306a36Sopenharmony_cistatic int arm_smmu_insert_master(struct arm_smmu_device *smmu, 257462306a36Sopenharmony_ci struct arm_smmu_master *master) 257562306a36Sopenharmony_ci{ 257662306a36Sopenharmony_ci int i; 257762306a36Sopenharmony_ci int ret = 0; 257862306a36Sopenharmony_ci struct arm_smmu_stream *new_stream, *cur_stream; 257962306a36Sopenharmony_ci struct rb_node **new_node, *parent_node = NULL; 258062306a36Sopenharmony_ci struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(master->dev); 258162306a36Sopenharmony_ci 258262306a36Sopenharmony_ci master->streams = kcalloc(fwspec->num_ids, sizeof(*master->streams), 258362306a36Sopenharmony_ci GFP_KERNEL); 258462306a36Sopenharmony_ci if (!master->streams) 258562306a36Sopenharmony_ci return -ENOMEM; 258662306a36Sopenharmony_ci master->num_streams = fwspec->num_ids; 258762306a36Sopenharmony_ci 258862306a36Sopenharmony_ci mutex_lock(&smmu->streams_mutex); 258962306a36Sopenharmony_ci for (i = 0; i < fwspec->num_ids; i++) { 259062306a36Sopenharmony_ci u32 sid = fwspec->ids[i]; 259162306a36Sopenharmony_ci 259262306a36Sopenharmony_ci new_stream = &master->streams[i]; 259362306a36Sopenharmony_ci new_stream->id = sid; 259462306a36Sopenharmony_ci new_stream->master = master; 259562306a36Sopenharmony_ci 259662306a36Sopenharmony_ci ret = arm_smmu_init_sid_strtab(smmu, sid); 259762306a36Sopenharmony_ci if (ret) 259862306a36Sopenharmony_ci break; 259962306a36Sopenharmony_ci 260062306a36Sopenharmony_ci /* Insert into SID tree */ 260162306a36Sopenharmony_ci new_node = &(smmu->streams.rb_node); 260262306a36Sopenharmony_ci while (*new_node) { 260362306a36Sopenharmony_ci cur_stream = rb_entry(*new_node, struct arm_smmu_stream, 260462306a36Sopenharmony_ci node); 260562306a36Sopenharmony_ci parent_node = *new_node; 260662306a36Sopenharmony_ci if (cur_stream->id > new_stream->id) { 260762306a36Sopenharmony_ci new_node = &((*new_node)->rb_left); 260862306a36Sopenharmony_ci } else if (cur_stream->id < new_stream->id) { 260962306a36Sopenharmony_ci new_node = &((*new_node)->rb_right); 261062306a36Sopenharmony_ci } else { 261162306a36Sopenharmony_ci dev_warn(master->dev, 261262306a36Sopenharmony_ci "stream %u already in tree\n", 261362306a36Sopenharmony_ci cur_stream->id); 261462306a36Sopenharmony_ci ret = -EINVAL; 261562306a36Sopenharmony_ci break; 261662306a36Sopenharmony_ci } 261762306a36Sopenharmony_ci } 261862306a36Sopenharmony_ci if (ret) 261962306a36Sopenharmony_ci break; 262062306a36Sopenharmony_ci 262162306a36Sopenharmony_ci rb_link_node(&new_stream->node, parent_node, new_node); 262262306a36Sopenharmony_ci rb_insert_color(&new_stream->node, &smmu->streams); 262362306a36Sopenharmony_ci } 262462306a36Sopenharmony_ci 262562306a36Sopenharmony_ci if (ret) { 262662306a36Sopenharmony_ci for (i--; i >= 0; i--) 262762306a36Sopenharmony_ci rb_erase(&master->streams[i].node, &smmu->streams); 262862306a36Sopenharmony_ci kfree(master->streams); 262962306a36Sopenharmony_ci } 263062306a36Sopenharmony_ci mutex_unlock(&smmu->streams_mutex); 263162306a36Sopenharmony_ci 263262306a36Sopenharmony_ci return ret; 263362306a36Sopenharmony_ci} 263462306a36Sopenharmony_ci 263562306a36Sopenharmony_cistatic void arm_smmu_remove_master(struct arm_smmu_master *master) 263662306a36Sopenharmony_ci{ 263762306a36Sopenharmony_ci int i; 263862306a36Sopenharmony_ci struct arm_smmu_device *smmu = master->smmu; 263962306a36Sopenharmony_ci struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(master->dev); 264062306a36Sopenharmony_ci 264162306a36Sopenharmony_ci if (!smmu || !master->streams) 264262306a36Sopenharmony_ci return; 264362306a36Sopenharmony_ci 264462306a36Sopenharmony_ci mutex_lock(&smmu->streams_mutex); 264562306a36Sopenharmony_ci for (i = 0; i < fwspec->num_ids; i++) 264662306a36Sopenharmony_ci rb_erase(&master->streams[i].node, &smmu->streams); 264762306a36Sopenharmony_ci mutex_unlock(&smmu->streams_mutex); 264862306a36Sopenharmony_ci 264962306a36Sopenharmony_ci kfree(master->streams); 265062306a36Sopenharmony_ci} 265162306a36Sopenharmony_ci 265262306a36Sopenharmony_cistatic struct iommu_ops arm_smmu_ops; 265362306a36Sopenharmony_ci 265462306a36Sopenharmony_cistatic struct iommu_device *arm_smmu_probe_device(struct device *dev) 265562306a36Sopenharmony_ci{ 265662306a36Sopenharmony_ci int ret; 265762306a36Sopenharmony_ci struct arm_smmu_device *smmu; 265862306a36Sopenharmony_ci struct arm_smmu_master *master; 265962306a36Sopenharmony_ci struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); 266062306a36Sopenharmony_ci 266162306a36Sopenharmony_ci if (!fwspec || fwspec->ops != &arm_smmu_ops) 266262306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 266362306a36Sopenharmony_ci 266462306a36Sopenharmony_ci if (WARN_ON_ONCE(dev_iommu_priv_get(dev))) 266562306a36Sopenharmony_ci return ERR_PTR(-EBUSY); 266662306a36Sopenharmony_ci 266762306a36Sopenharmony_ci smmu = arm_smmu_get_by_fwnode(fwspec->iommu_fwnode); 266862306a36Sopenharmony_ci if (!smmu) 266962306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 267062306a36Sopenharmony_ci 267162306a36Sopenharmony_ci master = kzalloc(sizeof(*master), GFP_KERNEL); 267262306a36Sopenharmony_ci if (!master) 267362306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 267462306a36Sopenharmony_ci 267562306a36Sopenharmony_ci master->dev = dev; 267662306a36Sopenharmony_ci master->smmu = smmu; 267762306a36Sopenharmony_ci INIT_LIST_HEAD(&master->bonds); 267862306a36Sopenharmony_ci dev_iommu_priv_set(dev, master); 267962306a36Sopenharmony_ci 268062306a36Sopenharmony_ci ret = arm_smmu_insert_master(smmu, master); 268162306a36Sopenharmony_ci if (ret) 268262306a36Sopenharmony_ci goto err_free_master; 268362306a36Sopenharmony_ci 268462306a36Sopenharmony_ci device_property_read_u32(dev, "pasid-num-bits", &master->ssid_bits); 268562306a36Sopenharmony_ci master->ssid_bits = min(smmu->ssid_bits, master->ssid_bits); 268662306a36Sopenharmony_ci 268762306a36Sopenharmony_ci /* 268862306a36Sopenharmony_ci * Note that PASID must be enabled before, and disabled after ATS: 268962306a36Sopenharmony_ci * PCI Express Base 4.0r1.0 - 10.5.1.3 ATS Control Register 269062306a36Sopenharmony_ci * 269162306a36Sopenharmony_ci * Behavior is undefined if this bit is Set and the value of the PASID 269262306a36Sopenharmony_ci * Enable, Execute Requested Enable, or Privileged Mode Requested bits 269362306a36Sopenharmony_ci * are changed. 269462306a36Sopenharmony_ci */ 269562306a36Sopenharmony_ci arm_smmu_enable_pasid(master); 269662306a36Sopenharmony_ci 269762306a36Sopenharmony_ci if (!(smmu->features & ARM_SMMU_FEAT_2_LVL_CDTAB)) 269862306a36Sopenharmony_ci master->ssid_bits = min_t(u8, master->ssid_bits, 269962306a36Sopenharmony_ci CTXDESC_LINEAR_CDMAX); 270062306a36Sopenharmony_ci 270162306a36Sopenharmony_ci if ((smmu->features & ARM_SMMU_FEAT_STALLS && 270262306a36Sopenharmony_ci device_property_read_bool(dev, "dma-can-stall")) || 270362306a36Sopenharmony_ci smmu->features & ARM_SMMU_FEAT_STALL_FORCE) 270462306a36Sopenharmony_ci master->stall_enabled = true; 270562306a36Sopenharmony_ci 270662306a36Sopenharmony_ci return &smmu->iommu; 270762306a36Sopenharmony_ci 270862306a36Sopenharmony_cierr_free_master: 270962306a36Sopenharmony_ci kfree(master); 271062306a36Sopenharmony_ci dev_iommu_priv_set(dev, NULL); 271162306a36Sopenharmony_ci return ERR_PTR(ret); 271262306a36Sopenharmony_ci} 271362306a36Sopenharmony_ci 271462306a36Sopenharmony_cistatic void arm_smmu_release_device(struct device *dev) 271562306a36Sopenharmony_ci{ 271662306a36Sopenharmony_ci struct arm_smmu_master *master = dev_iommu_priv_get(dev); 271762306a36Sopenharmony_ci 271862306a36Sopenharmony_ci if (WARN_ON(arm_smmu_master_sva_enabled(master))) 271962306a36Sopenharmony_ci iopf_queue_remove_device(master->smmu->evtq.iopf, dev); 272062306a36Sopenharmony_ci arm_smmu_detach_dev(master); 272162306a36Sopenharmony_ci arm_smmu_disable_pasid(master); 272262306a36Sopenharmony_ci arm_smmu_remove_master(master); 272362306a36Sopenharmony_ci kfree(master); 272462306a36Sopenharmony_ci} 272562306a36Sopenharmony_ci 272662306a36Sopenharmony_cistatic struct iommu_group *arm_smmu_device_group(struct device *dev) 272762306a36Sopenharmony_ci{ 272862306a36Sopenharmony_ci struct iommu_group *group; 272962306a36Sopenharmony_ci 273062306a36Sopenharmony_ci /* 273162306a36Sopenharmony_ci * We don't support devices sharing stream IDs other than PCI RID 273262306a36Sopenharmony_ci * aliases, since the necessary ID-to-device lookup becomes rather 273362306a36Sopenharmony_ci * impractical given a potential sparse 32-bit stream ID space. 273462306a36Sopenharmony_ci */ 273562306a36Sopenharmony_ci if (dev_is_pci(dev)) 273662306a36Sopenharmony_ci group = pci_device_group(dev); 273762306a36Sopenharmony_ci else 273862306a36Sopenharmony_ci group = generic_device_group(dev); 273962306a36Sopenharmony_ci 274062306a36Sopenharmony_ci return group; 274162306a36Sopenharmony_ci} 274262306a36Sopenharmony_ci 274362306a36Sopenharmony_cistatic int arm_smmu_enable_nesting(struct iommu_domain *domain) 274462306a36Sopenharmony_ci{ 274562306a36Sopenharmony_ci struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); 274662306a36Sopenharmony_ci int ret = 0; 274762306a36Sopenharmony_ci 274862306a36Sopenharmony_ci mutex_lock(&smmu_domain->init_mutex); 274962306a36Sopenharmony_ci if (smmu_domain->smmu) 275062306a36Sopenharmony_ci ret = -EPERM; 275162306a36Sopenharmony_ci else 275262306a36Sopenharmony_ci smmu_domain->stage = ARM_SMMU_DOMAIN_NESTED; 275362306a36Sopenharmony_ci mutex_unlock(&smmu_domain->init_mutex); 275462306a36Sopenharmony_ci 275562306a36Sopenharmony_ci return ret; 275662306a36Sopenharmony_ci} 275762306a36Sopenharmony_ci 275862306a36Sopenharmony_cistatic int arm_smmu_of_xlate(struct device *dev, struct of_phandle_args *args) 275962306a36Sopenharmony_ci{ 276062306a36Sopenharmony_ci return iommu_fwspec_add_ids(dev, args->args, 1); 276162306a36Sopenharmony_ci} 276262306a36Sopenharmony_ci 276362306a36Sopenharmony_cistatic void arm_smmu_get_resv_regions(struct device *dev, 276462306a36Sopenharmony_ci struct list_head *head) 276562306a36Sopenharmony_ci{ 276662306a36Sopenharmony_ci struct iommu_resv_region *region; 276762306a36Sopenharmony_ci int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO; 276862306a36Sopenharmony_ci 276962306a36Sopenharmony_ci region = iommu_alloc_resv_region(MSI_IOVA_BASE, MSI_IOVA_LENGTH, 277062306a36Sopenharmony_ci prot, IOMMU_RESV_SW_MSI, GFP_KERNEL); 277162306a36Sopenharmony_ci if (!region) 277262306a36Sopenharmony_ci return; 277362306a36Sopenharmony_ci 277462306a36Sopenharmony_ci list_add_tail(®ion->list, head); 277562306a36Sopenharmony_ci 277662306a36Sopenharmony_ci iommu_dma_get_resv_regions(dev, head); 277762306a36Sopenharmony_ci} 277862306a36Sopenharmony_ci 277962306a36Sopenharmony_cistatic int arm_smmu_dev_enable_feature(struct device *dev, 278062306a36Sopenharmony_ci enum iommu_dev_features feat) 278162306a36Sopenharmony_ci{ 278262306a36Sopenharmony_ci struct arm_smmu_master *master = dev_iommu_priv_get(dev); 278362306a36Sopenharmony_ci 278462306a36Sopenharmony_ci if (!master) 278562306a36Sopenharmony_ci return -ENODEV; 278662306a36Sopenharmony_ci 278762306a36Sopenharmony_ci switch (feat) { 278862306a36Sopenharmony_ci case IOMMU_DEV_FEAT_IOPF: 278962306a36Sopenharmony_ci if (!arm_smmu_master_iopf_supported(master)) 279062306a36Sopenharmony_ci return -EINVAL; 279162306a36Sopenharmony_ci if (master->iopf_enabled) 279262306a36Sopenharmony_ci return -EBUSY; 279362306a36Sopenharmony_ci master->iopf_enabled = true; 279462306a36Sopenharmony_ci return 0; 279562306a36Sopenharmony_ci case IOMMU_DEV_FEAT_SVA: 279662306a36Sopenharmony_ci if (!arm_smmu_master_sva_supported(master)) 279762306a36Sopenharmony_ci return -EINVAL; 279862306a36Sopenharmony_ci if (arm_smmu_master_sva_enabled(master)) 279962306a36Sopenharmony_ci return -EBUSY; 280062306a36Sopenharmony_ci return arm_smmu_master_enable_sva(master); 280162306a36Sopenharmony_ci default: 280262306a36Sopenharmony_ci return -EINVAL; 280362306a36Sopenharmony_ci } 280462306a36Sopenharmony_ci} 280562306a36Sopenharmony_ci 280662306a36Sopenharmony_cistatic int arm_smmu_dev_disable_feature(struct device *dev, 280762306a36Sopenharmony_ci enum iommu_dev_features feat) 280862306a36Sopenharmony_ci{ 280962306a36Sopenharmony_ci struct arm_smmu_master *master = dev_iommu_priv_get(dev); 281062306a36Sopenharmony_ci 281162306a36Sopenharmony_ci if (!master) 281262306a36Sopenharmony_ci return -EINVAL; 281362306a36Sopenharmony_ci 281462306a36Sopenharmony_ci switch (feat) { 281562306a36Sopenharmony_ci case IOMMU_DEV_FEAT_IOPF: 281662306a36Sopenharmony_ci if (!master->iopf_enabled) 281762306a36Sopenharmony_ci return -EINVAL; 281862306a36Sopenharmony_ci if (master->sva_enabled) 281962306a36Sopenharmony_ci return -EBUSY; 282062306a36Sopenharmony_ci master->iopf_enabled = false; 282162306a36Sopenharmony_ci return 0; 282262306a36Sopenharmony_ci case IOMMU_DEV_FEAT_SVA: 282362306a36Sopenharmony_ci if (!arm_smmu_master_sva_enabled(master)) 282462306a36Sopenharmony_ci return -EINVAL; 282562306a36Sopenharmony_ci return arm_smmu_master_disable_sva(master); 282662306a36Sopenharmony_ci default: 282762306a36Sopenharmony_ci return -EINVAL; 282862306a36Sopenharmony_ci } 282962306a36Sopenharmony_ci} 283062306a36Sopenharmony_ci 283162306a36Sopenharmony_ci/* 283262306a36Sopenharmony_ci * HiSilicon PCIe tune and trace device can be used to trace TLP headers on the 283362306a36Sopenharmony_ci * PCIe link and save the data to memory by DMA. The hardware is restricted to 283462306a36Sopenharmony_ci * use identity mapping only. 283562306a36Sopenharmony_ci */ 283662306a36Sopenharmony_ci#define IS_HISI_PTT_DEVICE(pdev) ((pdev)->vendor == PCI_VENDOR_ID_HUAWEI && \ 283762306a36Sopenharmony_ci (pdev)->device == 0xa12e) 283862306a36Sopenharmony_ci 283962306a36Sopenharmony_cistatic int arm_smmu_def_domain_type(struct device *dev) 284062306a36Sopenharmony_ci{ 284162306a36Sopenharmony_ci if (dev_is_pci(dev)) { 284262306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 284362306a36Sopenharmony_ci 284462306a36Sopenharmony_ci if (IS_HISI_PTT_DEVICE(pdev)) 284562306a36Sopenharmony_ci return IOMMU_DOMAIN_IDENTITY; 284662306a36Sopenharmony_ci } 284762306a36Sopenharmony_ci 284862306a36Sopenharmony_ci return 0; 284962306a36Sopenharmony_ci} 285062306a36Sopenharmony_ci 285162306a36Sopenharmony_cistatic void arm_smmu_remove_dev_pasid(struct device *dev, ioasid_t pasid) 285262306a36Sopenharmony_ci{ 285362306a36Sopenharmony_ci struct iommu_domain *domain; 285462306a36Sopenharmony_ci 285562306a36Sopenharmony_ci domain = iommu_get_domain_for_dev_pasid(dev, pasid, IOMMU_DOMAIN_SVA); 285662306a36Sopenharmony_ci if (WARN_ON(IS_ERR(domain)) || !domain) 285762306a36Sopenharmony_ci return; 285862306a36Sopenharmony_ci 285962306a36Sopenharmony_ci arm_smmu_sva_remove_dev_pasid(domain, dev, pasid); 286062306a36Sopenharmony_ci} 286162306a36Sopenharmony_ci 286262306a36Sopenharmony_cistatic struct iommu_ops arm_smmu_ops = { 286362306a36Sopenharmony_ci .capable = arm_smmu_capable, 286462306a36Sopenharmony_ci .domain_alloc = arm_smmu_domain_alloc, 286562306a36Sopenharmony_ci .probe_device = arm_smmu_probe_device, 286662306a36Sopenharmony_ci .release_device = arm_smmu_release_device, 286762306a36Sopenharmony_ci .device_group = arm_smmu_device_group, 286862306a36Sopenharmony_ci .of_xlate = arm_smmu_of_xlate, 286962306a36Sopenharmony_ci .get_resv_regions = arm_smmu_get_resv_regions, 287062306a36Sopenharmony_ci .remove_dev_pasid = arm_smmu_remove_dev_pasid, 287162306a36Sopenharmony_ci .dev_enable_feat = arm_smmu_dev_enable_feature, 287262306a36Sopenharmony_ci .dev_disable_feat = arm_smmu_dev_disable_feature, 287362306a36Sopenharmony_ci .page_response = arm_smmu_page_response, 287462306a36Sopenharmony_ci .def_domain_type = arm_smmu_def_domain_type, 287562306a36Sopenharmony_ci .pgsize_bitmap = -1UL, /* Restricted during device attach */ 287662306a36Sopenharmony_ci .owner = THIS_MODULE, 287762306a36Sopenharmony_ci .default_domain_ops = &(const struct iommu_domain_ops) { 287862306a36Sopenharmony_ci .attach_dev = arm_smmu_attach_dev, 287962306a36Sopenharmony_ci .map_pages = arm_smmu_map_pages, 288062306a36Sopenharmony_ci .unmap_pages = arm_smmu_unmap_pages, 288162306a36Sopenharmony_ci .flush_iotlb_all = arm_smmu_flush_iotlb_all, 288262306a36Sopenharmony_ci .iotlb_sync = arm_smmu_iotlb_sync, 288362306a36Sopenharmony_ci .iova_to_phys = arm_smmu_iova_to_phys, 288462306a36Sopenharmony_ci .enable_nesting = arm_smmu_enable_nesting, 288562306a36Sopenharmony_ci .free = arm_smmu_domain_free, 288662306a36Sopenharmony_ci } 288762306a36Sopenharmony_ci}; 288862306a36Sopenharmony_ci 288962306a36Sopenharmony_ci/* Probing and initialisation functions */ 289062306a36Sopenharmony_cistatic int arm_smmu_init_one_queue(struct arm_smmu_device *smmu, 289162306a36Sopenharmony_ci struct arm_smmu_queue *q, 289262306a36Sopenharmony_ci void __iomem *page, 289362306a36Sopenharmony_ci unsigned long prod_off, 289462306a36Sopenharmony_ci unsigned long cons_off, 289562306a36Sopenharmony_ci size_t dwords, const char *name) 289662306a36Sopenharmony_ci{ 289762306a36Sopenharmony_ci size_t qsz; 289862306a36Sopenharmony_ci 289962306a36Sopenharmony_ci do { 290062306a36Sopenharmony_ci qsz = ((1 << q->llq.max_n_shift) * dwords) << 3; 290162306a36Sopenharmony_ci q->base = dmam_alloc_coherent(smmu->dev, qsz, &q->base_dma, 290262306a36Sopenharmony_ci GFP_KERNEL); 290362306a36Sopenharmony_ci if (q->base || qsz < PAGE_SIZE) 290462306a36Sopenharmony_ci break; 290562306a36Sopenharmony_ci 290662306a36Sopenharmony_ci q->llq.max_n_shift--; 290762306a36Sopenharmony_ci } while (1); 290862306a36Sopenharmony_ci 290962306a36Sopenharmony_ci if (!q->base) { 291062306a36Sopenharmony_ci dev_err(smmu->dev, 291162306a36Sopenharmony_ci "failed to allocate queue (0x%zx bytes) for %s\n", 291262306a36Sopenharmony_ci qsz, name); 291362306a36Sopenharmony_ci return -ENOMEM; 291462306a36Sopenharmony_ci } 291562306a36Sopenharmony_ci 291662306a36Sopenharmony_ci if (!WARN_ON(q->base_dma & (qsz - 1))) { 291762306a36Sopenharmony_ci dev_info(smmu->dev, "allocated %u entries for %s\n", 291862306a36Sopenharmony_ci 1 << q->llq.max_n_shift, name); 291962306a36Sopenharmony_ci } 292062306a36Sopenharmony_ci 292162306a36Sopenharmony_ci q->prod_reg = page + prod_off; 292262306a36Sopenharmony_ci q->cons_reg = page + cons_off; 292362306a36Sopenharmony_ci q->ent_dwords = dwords; 292462306a36Sopenharmony_ci 292562306a36Sopenharmony_ci q->q_base = Q_BASE_RWA; 292662306a36Sopenharmony_ci q->q_base |= q->base_dma & Q_BASE_ADDR_MASK; 292762306a36Sopenharmony_ci q->q_base |= FIELD_PREP(Q_BASE_LOG2SIZE, q->llq.max_n_shift); 292862306a36Sopenharmony_ci 292962306a36Sopenharmony_ci q->llq.prod = q->llq.cons = 0; 293062306a36Sopenharmony_ci return 0; 293162306a36Sopenharmony_ci} 293262306a36Sopenharmony_ci 293362306a36Sopenharmony_cistatic int arm_smmu_cmdq_init(struct arm_smmu_device *smmu) 293462306a36Sopenharmony_ci{ 293562306a36Sopenharmony_ci struct arm_smmu_cmdq *cmdq = &smmu->cmdq; 293662306a36Sopenharmony_ci unsigned int nents = 1 << cmdq->q.llq.max_n_shift; 293762306a36Sopenharmony_ci 293862306a36Sopenharmony_ci atomic_set(&cmdq->owner_prod, 0); 293962306a36Sopenharmony_ci atomic_set(&cmdq->lock, 0); 294062306a36Sopenharmony_ci 294162306a36Sopenharmony_ci cmdq->valid_map = (atomic_long_t *)devm_bitmap_zalloc(smmu->dev, nents, 294262306a36Sopenharmony_ci GFP_KERNEL); 294362306a36Sopenharmony_ci if (!cmdq->valid_map) 294462306a36Sopenharmony_ci return -ENOMEM; 294562306a36Sopenharmony_ci 294662306a36Sopenharmony_ci return 0; 294762306a36Sopenharmony_ci} 294862306a36Sopenharmony_ci 294962306a36Sopenharmony_cistatic int arm_smmu_init_queues(struct arm_smmu_device *smmu) 295062306a36Sopenharmony_ci{ 295162306a36Sopenharmony_ci int ret; 295262306a36Sopenharmony_ci 295362306a36Sopenharmony_ci /* cmdq */ 295462306a36Sopenharmony_ci ret = arm_smmu_init_one_queue(smmu, &smmu->cmdq.q, smmu->base, 295562306a36Sopenharmony_ci ARM_SMMU_CMDQ_PROD, ARM_SMMU_CMDQ_CONS, 295662306a36Sopenharmony_ci CMDQ_ENT_DWORDS, "cmdq"); 295762306a36Sopenharmony_ci if (ret) 295862306a36Sopenharmony_ci return ret; 295962306a36Sopenharmony_ci 296062306a36Sopenharmony_ci ret = arm_smmu_cmdq_init(smmu); 296162306a36Sopenharmony_ci if (ret) 296262306a36Sopenharmony_ci return ret; 296362306a36Sopenharmony_ci 296462306a36Sopenharmony_ci /* evtq */ 296562306a36Sopenharmony_ci ret = arm_smmu_init_one_queue(smmu, &smmu->evtq.q, smmu->page1, 296662306a36Sopenharmony_ci ARM_SMMU_EVTQ_PROD, ARM_SMMU_EVTQ_CONS, 296762306a36Sopenharmony_ci EVTQ_ENT_DWORDS, "evtq"); 296862306a36Sopenharmony_ci if (ret) 296962306a36Sopenharmony_ci return ret; 297062306a36Sopenharmony_ci 297162306a36Sopenharmony_ci if ((smmu->features & ARM_SMMU_FEAT_SVA) && 297262306a36Sopenharmony_ci (smmu->features & ARM_SMMU_FEAT_STALLS)) { 297362306a36Sopenharmony_ci smmu->evtq.iopf = iopf_queue_alloc(dev_name(smmu->dev)); 297462306a36Sopenharmony_ci if (!smmu->evtq.iopf) 297562306a36Sopenharmony_ci return -ENOMEM; 297662306a36Sopenharmony_ci } 297762306a36Sopenharmony_ci 297862306a36Sopenharmony_ci /* priq */ 297962306a36Sopenharmony_ci if (!(smmu->features & ARM_SMMU_FEAT_PRI)) 298062306a36Sopenharmony_ci return 0; 298162306a36Sopenharmony_ci 298262306a36Sopenharmony_ci return arm_smmu_init_one_queue(smmu, &smmu->priq.q, smmu->page1, 298362306a36Sopenharmony_ci ARM_SMMU_PRIQ_PROD, ARM_SMMU_PRIQ_CONS, 298462306a36Sopenharmony_ci PRIQ_ENT_DWORDS, "priq"); 298562306a36Sopenharmony_ci} 298662306a36Sopenharmony_ci 298762306a36Sopenharmony_cistatic int arm_smmu_init_l1_strtab(struct arm_smmu_device *smmu) 298862306a36Sopenharmony_ci{ 298962306a36Sopenharmony_ci unsigned int i; 299062306a36Sopenharmony_ci struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg; 299162306a36Sopenharmony_ci void *strtab = smmu->strtab_cfg.strtab; 299262306a36Sopenharmony_ci 299362306a36Sopenharmony_ci cfg->l1_desc = devm_kcalloc(smmu->dev, cfg->num_l1_ents, 299462306a36Sopenharmony_ci sizeof(*cfg->l1_desc), GFP_KERNEL); 299562306a36Sopenharmony_ci if (!cfg->l1_desc) 299662306a36Sopenharmony_ci return -ENOMEM; 299762306a36Sopenharmony_ci 299862306a36Sopenharmony_ci for (i = 0; i < cfg->num_l1_ents; ++i) { 299962306a36Sopenharmony_ci arm_smmu_write_strtab_l1_desc(strtab, &cfg->l1_desc[i]); 300062306a36Sopenharmony_ci strtab += STRTAB_L1_DESC_DWORDS << 3; 300162306a36Sopenharmony_ci } 300262306a36Sopenharmony_ci 300362306a36Sopenharmony_ci return 0; 300462306a36Sopenharmony_ci} 300562306a36Sopenharmony_ci 300662306a36Sopenharmony_cistatic int arm_smmu_init_strtab_2lvl(struct arm_smmu_device *smmu) 300762306a36Sopenharmony_ci{ 300862306a36Sopenharmony_ci void *strtab; 300962306a36Sopenharmony_ci u64 reg; 301062306a36Sopenharmony_ci u32 size, l1size; 301162306a36Sopenharmony_ci struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg; 301262306a36Sopenharmony_ci 301362306a36Sopenharmony_ci /* Calculate the L1 size, capped to the SIDSIZE. */ 301462306a36Sopenharmony_ci size = STRTAB_L1_SZ_SHIFT - (ilog2(STRTAB_L1_DESC_DWORDS) + 3); 301562306a36Sopenharmony_ci size = min(size, smmu->sid_bits - STRTAB_SPLIT); 301662306a36Sopenharmony_ci cfg->num_l1_ents = 1 << size; 301762306a36Sopenharmony_ci 301862306a36Sopenharmony_ci size += STRTAB_SPLIT; 301962306a36Sopenharmony_ci if (size < smmu->sid_bits) 302062306a36Sopenharmony_ci dev_warn(smmu->dev, 302162306a36Sopenharmony_ci "2-level strtab only covers %u/%u bits of SID\n", 302262306a36Sopenharmony_ci size, smmu->sid_bits); 302362306a36Sopenharmony_ci 302462306a36Sopenharmony_ci l1size = cfg->num_l1_ents * (STRTAB_L1_DESC_DWORDS << 3); 302562306a36Sopenharmony_ci strtab = dmam_alloc_coherent(smmu->dev, l1size, &cfg->strtab_dma, 302662306a36Sopenharmony_ci GFP_KERNEL); 302762306a36Sopenharmony_ci if (!strtab) { 302862306a36Sopenharmony_ci dev_err(smmu->dev, 302962306a36Sopenharmony_ci "failed to allocate l1 stream table (%u bytes)\n", 303062306a36Sopenharmony_ci l1size); 303162306a36Sopenharmony_ci return -ENOMEM; 303262306a36Sopenharmony_ci } 303362306a36Sopenharmony_ci cfg->strtab = strtab; 303462306a36Sopenharmony_ci 303562306a36Sopenharmony_ci /* Configure strtab_base_cfg for 2 levels */ 303662306a36Sopenharmony_ci reg = FIELD_PREP(STRTAB_BASE_CFG_FMT, STRTAB_BASE_CFG_FMT_2LVL); 303762306a36Sopenharmony_ci reg |= FIELD_PREP(STRTAB_BASE_CFG_LOG2SIZE, size); 303862306a36Sopenharmony_ci reg |= FIELD_PREP(STRTAB_BASE_CFG_SPLIT, STRTAB_SPLIT); 303962306a36Sopenharmony_ci cfg->strtab_base_cfg = reg; 304062306a36Sopenharmony_ci 304162306a36Sopenharmony_ci return arm_smmu_init_l1_strtab(smmu); 304262306a36Sopenharmony_ci} 304362306a36Sopenharmony_ci 304462306a36Sopenharmony_cistatic int arm_smmu_init_strtab_linear(struct arm_smmu_device *smmu) 304562306a36Sopenharmony_ci{ 304662306a36Sopenharmony_ci void *strtab; 304762306a36Sopenharmony_ci u64 reg; 304862306a36Sopenharmony_ci u32 size; 304962306a36Sopenharmony_ci struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg; 305062306a36Sopenharmony_ci 305162306a36Sopenharmony_ci size = (1 << smmu->sid_bits) * (STRTAB_STE_DWORDS << 3); 305262306a36Sopenharmony_ci strtab = dmam_alloc_coherent(smmu->dev, size, &cfg->strtab_dma, 305362306a36Sopenharmony_ci GFP_KERNEL); 305462306a36Sopenharmony_ci if (!strtab) { 305562306a36Sopenharmony_ci dev_err(smmu->dev, 305662306a36Sopenharmony_ci "failed to allocate linear stream table (%u bytes)\n", 305762306a36Sopenharmony_ci size); 305862306a36Sopenharmony_ci return -ENOMEM; 305962306a36Sopenharmony_ci } 306062306a36Sopenharmony_ci cfg->strtab = strtab; 306162306a36Sopenharmony_ci cfg->num_l1_ents = 1 << smmu->sid_bits; 306262306a36Sopenharmony_ci 306362306a36Sopenharmony_ci /* Configure strtab_base_cfg for a linear table covering all SIDs */ 306462306a36Sopenharmony_ci reg = FIELD_PREP(STRTAB_BASE_CFG_FMT, STRTAB_BASE_CFG_FMT_LINEAR); 306562306a36Sopenharmony_ci reg |= FIELD_PREP(STRTAB_BASE_CFG_LOG2SIZE, smmu->sid_bits); 306662306a36Sopenharmony_ci cfg->strtab_base_cfg = reg; 306762306a36Sopenharmony_ci 306862306a36Sopenharmony_ci arm_smmu_init_bypass_stes(strtab, cfg->num_l1_ents, false); 306962306a36Sopenharmony_ci return 0; 307062306a36Sopenharmony_ci} 307162306a36Sopenharmony_ci 307262306a36Sopenharmony_cistatic int arm_smmu_init_strtab(struct arm_smmu_device *smmu) 307362306a36Sopenharmony_ci{ 307462306a36Sopenharmony_ci u64 reg; 307562306a36Sopenharmony_ci int ret; 307662306a36Sopenharmony_ci 307762306a36Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB) 307862306a36Sopenharmony_ci ret = arm_smmu_init_strtab_2lvl(smmu); 307962306a36Sopenharmony_ci else 308062306a36Sopenharmony_ci ret = arm_smmu_init_strtab_linear(smmu); 308162306a36Sopenharmony_ci 308262306a36Sopenharmony_ci if (ret) 308362306a36Sopenharmony_ci return ret; 308462306a36Sopenharmony_ci 308562306a36Sopenharmony_ci /* Set the strtab base address */ 308662306a36Sopenharmony_ci reg = smmu->strtab_cfg.strtab_dma & STRTAB_BASE_ADDR_MASK; 308762306a36Sopenharmony_ci reg |= STRTAB_BASE_RA; 308862306a36Sopenharmony_ci smmu->strtab_cfg.strtab_base = reg; 308962306a36Sopenharmony_ci 309062306a36Sopenharmony_ci ida_init(&smmu->vmid_map); 309162306a36Sopenharmony_ci 309262306a36Sopenharmony_ci return 0; 309362306a36Sopenharmony_ci} 309462306a36Sopenharmony_ci 309562306a36Sopenharmony_cistatic int arm_smmu_init_structures(struct arm_smmu_device *smmu) 309662306a36Sopenharmony_ci{ 309762306a36Sopenharmony_ci int ret; 309862306a36Sopenharmony_ci 309962306a36Sopenharmony_ci mutex_init(&smmu->streams_mutex); 310062306a36Sopenharmony_ci smmu->streams = RB_ROOT; 310162306a36Sopenharmony_ci 310262306a36Sopenharmony_ci ret = arm_smmu_init_queues(smmu); 310362306a36Sopenharmony_ci if (ret) 310462306a36Sopenharmony_ci return ret; 310562306a36Sopenharmony_ci 310662306a36Sopenharmony_ci return arm_smmu_init_strtab(smmu); 310762306a36Sopenharmony_ci} 310862306a36Sopenharmony_ci 310962306a36Sopenharmony_cistatic int arm_smmu_write_reg_sync(struct arm_smmu_device *smmu, u32 val, 311062306a36Sopenharmony_ci unsigned int reg_off, unsigned int ack_off) 311162306a36Sopenharmony_ci{ 311262306a36Sopenharmony_ci u32 reg; 311362306a36Sopenharmony_ci 311462306a36Sopenharmony_ci writel_relaxed(val, smmu->base + reg_off); 311562306a36Sopenharmony_ci return readl_relaxed_poll_timeout(smmu->base + ack_off, reg, reg == val, 311662306a36Sopenharmony_ci 1, ARM_SMMU_POLL_TIMEOUT_US); 311762306a36Sopenharmony_ci} 311862306a36Sopenharmony_ci 311962306a36Sopenharmony_ci/* GBPA is "special" */ 312062306a36Sopenharmony_cistatic int arm_smmu_update_gbpa(struct arm_smmu_device *smmu, u32 set, u32 clr) 312162306a36Sopenharmony_ci{ 312262306a36Sopenharmony_ci int ret; 312362306a36Sopenharmony_ci u32 reg, __iomem *gbpa = smmu->base + ARM_SMMU_GBPA; 312462306a36Sopenharmony_ci 312562306a36Sopenharmony_ci ret = readl_relaxed_poll_timeout(gbpa, reg, !(reg & GBPA_UPDATE), 312662306a36Sopenharmony_ci 1, ARM_SMMU_POLL_TIMEOUT_US); 312762306a36Sopenharmony_ci if (ret) 312862306a36Sopenharmony_ci return ret; 312962306a36Sopenharmony_ci 313062306a36Sopenharmony_ci reg &= ~clr; 313162306a36Sopenharmony_ci reg |= set; 313262306a36Sopenharmony_ci writel_relaxed(reg | GBPA_UPDATE, gbpa); 313362306a36Sopenharmony_ci ret = readl_relaxed_poll_timeout(gbpa, reg, !(reg & GBPA_UPDATE), 313462306a36Sopenharmony_ci 1, ARM_SMMU_POLL_TIMEOUT_US); 313562306a36Sopenharmony_ci 313662306a36Sopenharmony_ci if (ret) 313762306a36Sopenharmony_ci dev_err(smmu->dev, "GBPA not responding to update\n"); 313862306a36Sopenharmony_ci return ret; 313962306a36Sopenharmony_ci} 314062306a36Sopenharmony_ci 314162306a36Sopenharmony_cistatic void arm_smmu_free_msis(void *data) 314262306a36Sopenharmony_ci{ 314362306a36Sopenharmony_ci struct device *dev = data; 314462306a36Sopenharmony_ci platform_msi_domain_free_irqs(dev); 314562306a36Sopenharmony_ci} 314662306a36Sopenharmony_ci 314762306a36Sopenharmony_cistatic void arm_smmu_write_msi_msg(struct msi_desc *desc, struct msi_msg *msg) 314862306a36Sopenharmony_ci{ 314962306a36Sopenharmony_ci phys_addr_t doorbell; 315062306a36Sopenharmony_ci struct device *dev = msi_desc_to_dev(desc); 315162306a36Sopenharmony_ci struct arm_smmu_device *smmu = dev_get_drvdata(dev); 315262306a36Sopenharmony_ci phys_addr_t *cfg = arm_smmu_msi_cfg[desc->msi_index]; 315362306a36Sopenharmony_ci 315462306a36Sopenharmony_ci doorbell = (((u64)msg->address_hi) << 32) | msg->address_lo; 315562306a36Sopenharmony_ci doorbell &= MSI_CFG0_ADDR_MASK; 315662306a36Sopenharmony_ci 315762306a36Sopenharmony_ci writeq_relaxed(doorbell, smmu->base + cfg[0]); 315862306a36Sopenharmony_ci writel_relaxed(msg->data, smmu->base + cfg[1]); 315962306a36Sopenharmony_ci writel_relaxed(ARM_SMMU_MEMATTR_DEVICE_nGnRE, smmu->base + cfg[2]); 316062306a36Sopenharmony_ci} 316162306a36Sopenharmony_ci 316262306a36Sopenharmony_cistatic void arm_smmu_setup_msis(struct arm_smmu_device *smmu) 316362306a36Sopenharmony_ci{ 316462306a36Sopenharmony_ci int ret, nvec = ARM_SMMU_MAX_MSIS; 316562306a36Sopenharmony_ci struct device *dev = smmu->dev; 316662306a36Sopenharmony_ci 316762306a36Sopenharmony_ci /* Clear the MSI address regs */ 316862306a36Sopenharmony_ci writeq_relaxed(0, smmu->base + ARM_SMMU_GERROR_IRQ_CFG0); 316962306a36Sopenharmony_ci writeq_relaxed(0, smmu->base + ARM_SMMU_EVTQ_IRQ_CFG0); 317062306a36Sopenharmony_ci 317162306a36Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_PRI) 317262306a36Sopenharmony_ci writeq_relaxed(0, smmu->base + ARM_SMMU_PRIQ_IRQ_CFG0); 317362306a36Sopenharmony_ci else 317462306a36Sopenharmony_ci nvec--; 317562306a36Sopenharmony_ci 317662306a36Sopenharmony_ci if (!(smmu->features & ARM_SMMU_FEAT_MSI)) 317762306a36Sopenharmony_ci return; 317862306a36Sopenharmony_ci 317962306a36Sopenharmony_ci if (!dev->msi.domain) { 318062306a36Sopenharmony_ci dev_info(smmu->dev, "msi_domain absent - falling back to wired irqs\n"); 318162306a36Sopenharmony_ci return; 318262306a36Sopenharmony_ci } 318362306a36Sopenharmony_ci 318462306a36Sopenharmony_ci /* Allocate MSIs for evtq, gerror and priq. Ignore cmdq */ 318562306a36Sopenharmony_ci ret = platform_msi_domain_alloc_irqs(dev, nvec, arm_smmu_write_msi_msg); 318662306a36Sopenharmony_ci if (ret) { 318762306a36Sopenharmony_ci dev_warn(dev, "failed to allocate MSIs - falling back to wired irqs\n"); 318862306a36Sopenharmony_ci return; 318962306a36Sopenharmony_ci } 319062306a36Sopenharmony_ci 319162306a36Sopenharmony_ci smmu->evtq.q.irq = msi_get_virq(dev, EVTQ_MSI_INDEX); 319262306a36Sopenharmony_ci smmu->gerr_irq = msi_get_virq(dev, GERROR_MSI_INDEX); 319362306a36Sopenharmony_ci smmu->priq.q.irq = msi_get_virq(dev, PRIQ_MSI_INDEX); 319462306a36Sopenharmony_ci 319562306a36Sopenharmony_ci /* Add callback to free MSIs on teardown */ 319662306a36Sopenharmony_ci devm_add_action(dev, arm_smmu_free_msis, dev); 319762306a36Sopenharmony_ci} 319862306a36Sopenharmony_ci 319962306a36Sopenharmony_cistatic void arm_smmu_setup_unique_irqs(struct arm_smmu_device *smmu) 320062306a36Sopenharmony_ci{ 320162306a36Sopenharmony_ci int irq, ret; 320262306a36Sopenharmony_ci 320362306a36Sopenharmony_ci arm_smmu_setup_msis(smmu); 320462306a36Sopenharmony_ci 320562306a36Sopenharmony_ci /* Request interrupt lines */ 320662306a36Sopenharmony_ci irq = smmu->evtq.q.irq; 320762306a36Sopenharmony_ci if (irq) { 320862306a36Sopenharmony_ci ret = devm_request_threaded_irq(smmu->dev, irq, NULL, 320962306a36Sopenharmony_ci arm_smmu_evtq_thread, 321062306a36Sopenharmony_ci IRQF_ONESHOT, 321162306a36Sopenharmony_ci "arm-smmu-v3-evtq", smmu); 321262306a36Sopenharmony_ci if (ret < 0) 321362306a36Sopenharmony_ci dev_warn(smmu->dev, "failed to enable evtq irq\n"); 321462306a36Sopenharmony_ci } else { 321562306a36Sopenharmony_ci dev_warn(smmu->dev, "no evtq irq - events will not be reported!\n"); 321662306a36Sopenharmony_ci } 321762306a36Sopenharmony_ci 321862306a36Sopenharmony_ci irq = smmu->gerr_irq; 321962306a36Sopenharmony_ci if (irq) { 322062306a36Sopenharmony_ci ret = devm_request_irq(smmu->dev, irq, arm_smmu_gerror_handler, 322162306a36Sopenharmony_ci 0, "arm-smmu-v3-gerror", smmu); 322262306a36Sopenharmony_ci if (ret < 0) 322362306a36Sopenharmony_ci dev_warn(smmu->dev, "failed to enable gerror irq\n"); 322462306a36Sopenharmony_ci } else { 322562306a36Sopenharmony_ci dev_warn(smmu->dev, "no gerr irq - errors will not be reported!\n"); 322662306a36Sopenharmony_ci } 322762306a36Sopenharmony_ci 322862306a36Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_PRI) { 322962306a36Sopenharmony_ci irq = smmu->priq.q.irq; 323062306a36Sopenharmony_ci if (irq) { 323162306a36Sopenharmony_ci ret = devm_request_threaded_irq(smmu->dev, irq, NULL, 323262306a36Sopenharmony_ci arm_smmu_priq_thread, 323362306a36Sopenharmony_ci IRQF_ONESHOT, 323462306a36Sopenharmony_ci "arm-smmu-v3-priq", 323562306a36Sopenharmony_ci smmu); 323662306a36Sopenharmony_ci if (ret < 0) 323762306a36Sopenharmony_ci dev_warn(smmu->dev, 323862306a36Sopenharmony_ci "failed to enable priq irq\n"); 323962306a36Sopenharmony_ci } else { 324062306a36Sopenharmony_ci dev_warn(smmu->dev, "no priq irq - PRI will be broken\n"); 324162306a36Sopenharmony_ci } 324262306a36Sopenharmony_ci } 324362306a36Sopenharmony_ci} 324462306a36Sopenharmony_ci 324562306a36Sopenharmony_cistatic int arm_smmu_setup_irqs(struct arm_smmu_device *smmu) 324662306a36Sopenharmony_ci{ 324762306a36Sopenharmony_ci int ret, irq; 324862306a36Sopenharmony_ci u32 irqen_flags = IRQ_CTRL_EVTQ_IRQEN | IRQ_CTRL_GERROR_IRQEN; 324962306a36Sopenharmony_ci 325062306a36Sopenharmony_ci /* Disable IRQs first */ 325162306a36Sopenharmony_ci ret = arm_smmu_write_reg_sync(smmu, 0, ARM_SMMU_IRQ_CTRL, 325262306a36Sopenharmony_ci ARM_SMMU_IRQ_CTRLACK); 325362306a36Sopenharmony_ci if (ret) { 325462306a36Sopenharmony_ci dev_err(smmu->dev, "failed to disable irqs\n"); 325562306a36Sopenharmony_ci return ret; 325662306a36Sopenharmony_ci } 325762306a36Sopenharmony_ci 325862306a36Sopenharmony_ci irq = smmu->combined_irq; 325962306a36Sopenharmony_ci if (irq) { 326062306a36Sopenharmony_ci /* 326162306a36Sopenharmony_ci * Cavium ThunderX2 implementation doesn't support unique irq 326262306a36Sopenharmony_ci * lines. Use a single irq line for all the SMMUv3 interrupts. 326362306a36Sopenharmony_ci */ 326462306a36Sopenharmony_ci ret = devm_request_threaded_irq(smmu->dev, irq, 326562306a36Sopenharmony_ci arm_smmu_combined_irq_handler, 326662306a36Sopenharmony_ci arm_smmu_combined_irq_thread, 326762306a36Sopenharmony_ci IRQF_ONESHOT, 326862306a36Sopenharmony_ci "arm-smmu-v3-combined-irq", smmu); 326962306a36Sopenharmony_ci if (ret < 0) 327062306a36Sopenharmony_ci dev_warn(smmu->dev, "failed to enable combined irq\n"); 327162306a36Sopenharmony_ci } else 327262306a36Sopenharmony_ci arm_smmu_setup_unique_irqs(smmu); 327362306a36Sopenharmony_ci 327462306a36Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_PRI) 327562306a36Sopenharmony_ci irqen_flags |= IRQ_CTRL_PRIQ_IRQEN; 327662306a36Sopenharmony_ci 327762306a36Sopenharmony_ci /* Enable interrupt generation on the SMMU */ 327862306a36Sopenharmony_ci ret = arm_smmu_write_reg_sync(smmu, irqen_flags, 327962306a36Sopenharmony_ci ARM_SMMU_IRQ_CTRL, ARM_SMMU_IRQ_CTRLACK); 328062306a36Sopenharmony_ci if (ret) 328162306a36Sopenharmony_ci dev_warn(smmu->dev, "failed to enable irqs\n"); 328262306a36Sopenharmony_ci 328362306a36Sopenharmony_ci return 0; 328462306a36Sopenharmony_ci} 328562306a36Sopenharmony_ci 328662306a36Sopenharmony_cistatic int arm_smmu_device_disable(struct arm_smmu_device *smmu) 328762306a36Sopenharmony_ci{ 328862306a36Sopenharmony_ci int ret; 328962306a36Sopenharmony_ci 329062306a36Sopenharmony_ci ret = arm_smmu_write_reg_sync(smmu, 0, ARM_SMMU_CR0, ARM_SMMU_CR0ACK); 329162306a36Sopenharmony_ci if (ret) 329262306a36Sopenharmony_ci dev_err(smmu->dev, "failed to clear cr0\n"); 329362306a36Sopenharmony_ci 329462306a36Sopenharmony_ci return ret; 329562306a36Sopenharmony_ci} 329662306a36Sopenharmony_ci 329762306a36Sopenharmony_cistatic int arm_smmu_device_reset(struct arm_smmu_device *smmu, bool bypass) 329862306a36Sopenharmony_ci{ 329962306a36Sopenharmony_ci int ret; 330062306a36Sopenharmony_ci u32 reg, enables; 330162306a36Sopenharmony_ci struct arm_smmu_cmdq_ent cmd; 330262306a36Sopenharmony_ci 330362306a36Sopenharmony_ci /* Clear CR0 and sync (disables SMMU and queue processing) */ 330462306a36Sopenharmony_ci reg = readl_relaxed(smmu->base + ARM_SMMU_CR0); 330562306a36Sopenharmony_ci if (reg & CR0_SMMUEN) { 330662306a36Sopenharmony_ci dev_warn(smmu->dev, "SMMU currently enabled! Resetting...\n"); 330762306a36Sopenharmony_ci WARN_ON(is_kdump_kernel() && !disable_bypass); 330862306a36Sopenharmony_ci arm_smmu_update_gbpa(smmu, GBPA_ABORT, 0); 330962306a36Sopenharmony_ci } 331062306a36Sopenharmony_ci 331162306a36Sopenharmony_ci ret = arm_smmu_device_disable(smmu); 331262306a36Sopenharmony_ci if (ret) 331362306a36Sopenharmony_ci return ret; 331462306a36Sopenharmony_ci 331562306a36Sopenharmony_ci /* CR1 (table and queue memory attributes) */ 331662306a36Sopenharmony_ci reg = FIELD_PREP(CR1_TABLE_SH, ARM_SMMU_SH_ISH) | 331762306a36Sopenharmony_ci FIELD_PREP(CR1_TABLE_OC, CR1_CACHE_WB) | 331862306a36Sopenharmony_ci FIELD_PREP(CR1_TABLE_IC, CR1_CACHE_WB) | 331962306a36Sopenharmony_ci FIELD_PREP(CR1_QUEUE_SH, ARM_SMMU_SH_ISH) | 332062306a36Sopenharmony_ci FIELD_PREP(CR1_QUEUE_OC, CR1_CACHE_WB) | 332162306a36Sopenharmony_ci FIELD_PREP(CR1_QUEUE_IC, CR1_CACHE_WB); 332262306a36Sopenharmony_ci writel_relaxed(reg, smmu->base + ARM_SMMU_CR1); 332362306a36Sopenharmony_ci 332462306a36Sopenharmony_ci /* CR2 (random crap) */ 332562306a36Sopenharmony_ci reg = CR2_PTM | CR2_RECINVSID; 332662306a36Sopenharmony_ci 332762306a36Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_E2H) 332862306a36Sopenharmony_ci reg |= CR2_E2H; 332962306a36Sopenharmony_ci 333062306a36Sopenharmony_ci writel_relaxed(reg, smmu->base + ARM_SMMU_CR2); 333162306a36Sopenharmony_ci 333262306a36Sopenharmony_ci /* Stream table */ 333362306a36Sopenharmony_ci writeq_relaxed(smmu->strtab_cfg.strtab_base, 333462306a36Sopenharmony_ci smmu->base + ARM_SMMU_STRTAB_BASE); 333562306a36Sopenharmony_ci writel_relaxed(smmu->strtab_cfg.strtab_base_cfg, 333662306a36Sopenharmony_ci smmu->base + ARM_SMMU_STRTAB_BASE_CFG); 333762306a36Sopenharmony_ci 333862306a36Sopenharmony_ci /* Command queue */ 333962306a36Sopenharmony_ci writeq_relaxed(smmu->cmdq.q.q_base, smmu->base + ARM_SMMU_CMDQ_BASE); 334062306a36Sopenharmony_ci writel_relaxed(smmu->cmdq.q.llq.prod, smmu->base + ARM_SMMU_CMDQ_PROD); 334162306a36Sopenharmony_ci writel_relaxed(smmu->cmdq.q.llq.cons, smmu->base + ARM_SMMU_CMDQ_CONS); 334262306a36Sopenharmony_ci 334362306a36Sopenharmony_ci enables = CR0_CMDQEN; 334462306a36Sopenharmony_ci ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0, 334562306a36Sopenharmony_ci ARM_SMMU_CR0ACK); 334662306a36Sopenharmony_ci if (ret) { 334762306a36Sopenharmony_ci dev_err(smmu->dev, "failed to enable command queue\n"); 334862306a36Sopenharmony_ci return ret; 334962306a36Sopenharmony_ci } 335062306a36Sopenharmony_ci 335162306a36Sopenharmony_ci /* Invalidate any cached configuration */ 335262306a36Sopenharmony_ci cmd.opcode = CMDQ_OP_CFGI_ALL; 335362306a36Sopenharmony_ci arm_smmu_cmdq_issue_cmd_with_sync(smmu, &cmd); 335462306a36Sopenharmony_ci 335562306a36Sopenharmony_ci /* Invalidate any stale TLB entries */ 335662306a36Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_HYP) { 335762306a36Sopenharmony_ci cmd.opcode = CMDQ_OP_TLBI_EL2_ALL; 335862306a36Sopenharmony_ci arm_smmu_cmdq_issue_cmd_with_sync(smmu, &cmd); 335962306a36Sopenharmony_ci } 336062306a36Sopenharmony_ci 336162306a36Sopenharmony_ci cmd.opcode = CMDQ_OP_TLBI_NSNH_ALL; 336262306a36Sopenharmony_ci arm_smmu_cmdq_issue_cmd_with_sync(smmu, &cmd); 336362306a36Sopenharmony_ci 336462306a36Sopenharmony_ci /* Event queue */ 336562306a36Sopenharmony_ci writeq_relaxed(smmu->evtq.q.q_base, smmu->base + ARM_SMMU_EVTQ_BASE); 336662306a36Sopenharmony_ci writel_relaxed(smmu->evtq.q.llq.prod, smmu->page1 + ARM_SMMU_EVTQ_PROD); 336762306a36Sopenharmony_ci writel_relaxed(smmu->evtq.q.llq.cons, smmu->page1 + ARM_SMMU_EVTQ_CONS); 336862306a36Sopenharmony_ci 336962306a36Sopenharmony_ci enables |= CR0_EVTQEN; 337062306a36Sopenharmony_ci ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0, 337162306a36Sopenharmony_ci ARM_SMMU_CR0ACK); 337262306a36Sopenharmony_ci if (ret) { 337362306a36Sopenharmony_ci dev_err(smmu->dev, "failed to enable event queue\n"); 337462306a36Sopenharmony_ci return ret; 337562306a36Sopenharmony_ci } 337662306a36Sopenharmony_ci 337762306a36Sopenharmony_ci /* PRI queue */ 337862306a36Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_PRI) { 337962306a36Sopenharmony_ci writeq_relaxed(smmu->priq.q.q_base, 338062306a36Sopenharmony_ci smmu->base + ARM_SMMU_PRIQ_BASE); 338162306a36Sopenharmony_ci writel_relaxed(smmu->priq.q.llq.prod, 338262306a36Sopenharmony_ci smmu->page1 + ARM_SMMU_PRIQ_PROD); 338362306a36Sopenharmony_ci writel_relaxed(smmu->priq.q.llq.cons, 338462306a36Sopenharmony_ci smmu->page1 + ARM_SMMU_PRIQ_CONS); 338562306a36Sopenharmony_ci 338662306a36Sopenharmony_ci enables |= CR0_PRIQEN; 338762306a36Sopenharmony_ci ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0, 338862306a36Sopenharmony_ci ARM_SMMU_CR0ACK); 338962306a36Sopenharmony_ci if (ret) { 339062306a36Sopenharmony_ci dev_err(smmu->dev, "failed to enable PRI queue\n"); 339162306a36Sopenharmony_ci return ret; 339262306a36Sopenharmony_ci } 339362306a36Sopenharmony_ci } 339462306a36Sopenharmony_ci 339562306a36Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_ATS) { 339662306a36Sopenharmony_ci enables |= CR0_ATSCHK; 339762306a36Sopenharmony_ci ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0, 339862306a36Sopenharmony_ci ARM_SMMU_CR0ACK); 339962306a36Sopenharmony_ci if (ret) { 340062306a36Sopenharmony_ci dev_err(smmu->dev, "failed to enable ATS check\n"); 340162306a36Sopenharmony_ci return ret; 340262306a36Sopenharmony_ci } 340362306a36Sopenharmony_ci } 340462306a36Sopenharmony_ci 340562306a36Sopenharmony_ci ret = arm_smmu_setup_irqs(smmu); 340662306a36Sopenharmony_ci if (ret) { 340762306a36Sopenharmony_ci dev_err(smmu->dev, "failed to setup irqs\n"); 340862306a36Sopenharmony_ci return ret; 340962306a36Sopenharmony_ci } 341062306a36Sopenharmony_ci 341162306a36Sopenharmony_ci if (is_kdump_kernel()) 341262306a36Sopenharmony_ci enables &= ~(CR0_EVTQEN | CR0_PRIQEN); 341362306a36Sopenharmony_ci 341462306a36Sopenharmony_ci /* Enable the SMMU interface, or ensure bypass */ 341562306a36Sopenharmony_ci if (!bypass || disable_bypass) { 341662306a36Sopenharmony_ci enables |= CR0_SMMUEN; 341762306a36Sopenharmony_ci } else { 341862306a36Sopenharmony_ci ret = arm_smmu_update_gbpa(smmu, 0, GBPA_ABORT); 341962306a36Sopenharmony_ci if (ret) 342062306a36Sopenharmony_ci return ret; 342162306a36Sopenharmony_ci } 342262306a36Sopenharmony_ci ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0, 342362306a36Sopenharmony_ci ARM_SMMU_CR0ACK); 342462306a36Sopenharmony_ci if (ret) { 342562306a36Sopenharmony_ci dev_err(smmu->dev, "failed to enable SMMU interface\n"); 342662306a36Sopenharmony_ci return ret; 342762306a36Sopenharmony_ci } 342862306a36Sopenharmony_ci 342962306a36Sopenharmony_ci return 0; 343062306a36Sopenharmony_ci} 343162306a36Sopenharmony_ci 343262306a36Sopenharmony_ci#define IIDR_IMPLEMENTER_ARM 0x43b 343362306a36Sopenharmony_ci#define IIDR_PRODUCTID_ARM_MMU_600 0x483 343462306a36Sopenharmony_ci#define IIDR_PRODUCTID_ARM_MMU_700 0x487 343562306a36Sopenharmony_ci 343662306a36Sopenharmony_cistatic void arm_smmu_device_iidr_probe(struct arm_smmu_device *smmu) 343762306a36Sopenharmony_ci{ 343862306a36Sopenharmony_ci u32 reg; 343962306a36Sopenharmony_ci unsigned int implementer, productid, variant, revision; 344062306a36Sopenharmony_ci 344162306a36Sopenharmony_ci reg = readl_relaxed(smmu->base + ARM_SMMU_IIDR); 344262306a36Sopenharmony_ci implementer = FIELD_GET(IIDR_IMPLEMENTER, reg); 344362306a36Sopenharmony_ci productid = FIELD_GET(IIDR_PRODUCTID, reg); 344462306a36Sopenharmony_ci variant = FIELD_GET(IIDR_VARIANT, reg); 344562306a36Sopenharmony_ci revision = FIELD_GET(IIDR_REVISION, reg); 344662306a36Sopenharmony_ci 344762306a36Sopenharmony_ci switch (implementer) { 344862306a36Sopenharmony_ci case IIDR_IMPLEMENTER_ARM: 344962306a36Sopenharmony_ci switch (productid) { 345062306a36Sopenharmony_ci case IIDR_PRODUCTID_ARM_MMU_600: 345162306a36Sopenharmony_ci /* Arm erratum 1076982 */ 345262306a36Sopenharmony_ci if (variant == 0 && revision <= 2) 345362306a36Sopenharmony_ci smmu->features &= ~ARM_SMMU_FEAT_SEV; 345462306a36Sopenharmony_ci /* Arm erratum 1209401 */ 345562306a36Sopenharmony_ci if (variant < 2) 345662306a36Sopenharmony_ci smmu->features &= ~ARM_SMMU_FEAT_NESTING; 345762306a36Sopenharmony_ci break; 345862306a36Sopenharmony_ci case IIDR_PRODUCTID_ARM_MMU_700: 345962306a36Sopenharmony_ci /* Arm erratum 2812531 */ 346062306a36Sopenharmony_ci smmu->features &= ~ARM_SMMU_FEAT_BTM; 346162306a36Sopenharmony_ci smmu->options |= ARM_SMMU_OPT_CMDQ_FORCE_SYNC; 346262306a36Sopenharmony_ci /* Arm errata 2268618, 2812531 */ 346362306a36Sopenharmony_ci smmu->features &= ~ARM_SMMU_FEAT_NESTING; 346462306a36Sopenharmony_ci break; 346562306a36Sopenharmony_ci } 346662306a36Sopenharmony_ci break; 346762306a36Sopenharmony_ci } 346862306a36Sopenharmony_ci} 346962306a36Sopenharmony_ci 347062306a36Sopenharmony_cistatic int arm_smmu_device_hw_probe(struct arm_smmu_device *smmu) 347162306a36Sopenharmony_ci{ 347262306a36Sopenharmony_ci u32 reg; 347362306a36Sopenharmony_ci bool coherent = smmu->features & ARM_SMMU_FEAT_COHERENCY; 347462306a36Sopenharmony_ci 347562306a36Sopenharmony_ci /* IDR0 */ 347662306a36Sopenharmony_ci reg = readl_relaxed(smmu->base + ARM_SMMU_IDR0); 347762306a36Sopenharmony_ci 347862306a36Sopenharmony_ci /* 2-level structures */ 347962306a36Sopenharmony_ci if (FIELD_GET(IDR0_ST_LVL, reg) == IDR0_ST_LVL_2LVL) 348062306a36Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_2_LVL_STRTAB; 348162306a36Sopenharmony_ci 348262306a36Sopenharmony_ci if (reg & IDR0_CD2L) 348362306a36Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_2_LVL_CDTAB; 348462306a36Sopenharmony_ci 348562306a36Sopenharmony_ci /* 348662306a36Sopenharmony_ci * Translation table endianness. 348762306a36Sopenharmony_ci * We currently require the same endianness as the CPU, but this 348862306a36Sopenharmony_ci * could be changed later by adding a new IO_PGTABLE_QUIRK. 348962306a36Sopenharmony_ci */ 349062306a36Sopenharmony_ci switch (FIELD_GET(IDR0_TTENDIAN, reg)) { 349162306a36Sopenharmony_ci case IDR0_TTENDIAN_MIXED: 349262306a36Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_TT_LE | ARM_SMMU_FEAT_TT_BE; 349362306a36Sopenharmony_ci break; 349462306a36Sopenharmony_ci#ifdef __BIG_ENDIAN 349562306a36Sopenharmony_ci case IDR0_TTENDIAN_BE: 349662306a36Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_TT_BE; 349762306a36Sopenharmony_ci break; 349862306a36Sopenharmony_ci#else 349962306a36Sopenharmony_ci case IDR0_TTENDIAN_LE: 350062306a36Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_TT_LE; 350162306a36Sopenharmony_ci break; 350262306a36Sopenharmony_ci#endif 350362306a36Sopenharmony_ci default: 350462306a36Sopenharmony_ci dev_err(smmu->dev, "unknown/unsupported TT endianness!\n"); 350562306a36Sopenharmony_ci return -ENXIO; 350662306a36Sopenharmony_ci } 350762306a36Sopenharmony_ci 350862306a36Sopenharmony_ci /* Boolean feature flags */ 350962306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_PCI_PRI) && reg & IDR0_PRI) 351062306a36Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_PRI; 351162306a36Sopenharmony_ci 351262306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_PCI_ATS) && reg & IDR0_ATS) 351362306a36Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_ATS; 351462306a36Sopenharmony_ci 351562306a36Sopenharmony_ci if (reg & IDR0_SEV) 351662306a36Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_SEV; 351762306a36Sopenharmony_ci 351862306a36Sopenharmony_ci if (reg & IDR0_MSI) { 351962306a36Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_MSI; 352062306a36Sopenharmony_ci if (coherent && !disable_msipolling) 352162306a36Sopenharmony_ci smmu->options |= ARM_SMMU_OPT_MSIPOLL; 352262306a36Sopenharmony_ci } 352362306a36Sopenharmony_ci 352462306a36Sopenharmony_ci if (reg & IDR0_HYP) { 352562306a36Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_HYP; 352662306a36Sopenharmony_ci if (cpus_have_cap(ARM64_HAS_VIRT_HOST_EXTN)) 352762306a36Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_E2H; 352862306a36Sopenharmony_ci } 352962306a36Sopenharmony_ci 353062306a36Sopenharmony_ci /* 353162306a36Sopenharmony_ci * The coherency feature as set by FW is used in preference to the ID 353262306a36Sopenharmony_ci * register, but warn on mismatch. 353362306a36Sopenharmony_ci */ 353462306a36Sopenharmony_ci if (!!(reg & IDR0_COHACC) != coherent) 353562306a36Sopenharmony_ci dev_warn(smmu->dev, "IDR0.COHACC overridden by FW configuration (%s)\n", 353662306a36Sopenharmony_ci coherent ? "true" : "false"); 353762306a36Sopenharmony_ci 353862306a36Sopenharmony_ci switch (FIELD_GET(IDR0_STALL_MODEL, reg)) { 353962306a36Sopenharmony_ci case IDR0_STALL_MODEL_FORCE: 354062306a36Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_STALL_FORCE; 354162306a36Sopenharmony_ci fallthrough; 354262306a36Sopenharmony_ci case IDR0_STALL_MODEL_STALL: 354362306a36Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_STALLS; 354462306a36Sopenharmony_ci } 354562306a36Sopenharmony_ci 354662306a36Sopenharmony_ci if (reg & IDR0_S1P) 354762306a36Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_TRANS_S1; 354862306a36Sopenharmony_ci 354962306a36Sopenharmony_ci if (reg & IDR0_S2P) 355062306a36Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_TRANS_S2; 355162306a36Sopenharmony_ci 355262306a36Sopenharmony_ci if (!(reg & (IDR0_S1P | IDR0_S2P))) { 355362306a36Sopenharmony_ci dev_err(smmu->dev, "no translation support!\n"); 355462306a36Sopenharmony_ci return -ENXIO; 355562306a36Sopenharmony_ci } 355662306a36Sopenharmony_ci 355762306a36Sopenharmony_ci /* We only support the AArch64 table format at present */ 355862306a36Sopenharmony_ci switch (FIELD_GET(IDR0_TTF, reg)) { 355962306a36Sopenharmony_ci case IDR0_TTF_AARCH32_64: 356062306a36Sopenharmony_ci smmu->ias = 40; 356162306a36Sopenharmony_ci fallthrough; 356262306a36Sopenharmony_ci case IDR0_TTF_AARCH64: 356362306a36Sopenharmony_ci break; 356462306a36Sopenharmony_ci default: 356562306a36Sopenharmony_ci dev_err(smmu->dev, "AArch64 table format not supported!\n"); 356662306a36Sopenharmony_ci return -ENXIO; 356762306a36Sopenharmony_ci } 356862306a36Sopenharmony_ci 356962306a36Sopenharmony_ci /* ASID/VMID sizes */ 357062306a36Sopenharmony_ci smmu->asid_bits = reg & IDR0_ASID16 ? 16 : 8; 357162306a36Sopenharmony_ci smmu->vmid_bits = reg & IDR0_VMID16 ? 16 : 8; 357262306a36Sopenharmony_ci 357362306a36Sopenharmony_ci /* IDR1 */ 357462306a36Sopenharmony_ci reg = readl_relaxed(smmu->base + ARM_SMMU_IDR1); 357562306a36Sopenharmony_ci if (reg & (IDR1_TABLES_PRESET | IDR1_QUEUES_PRESET | IDR1_REL)) { 357662306a36Sopenharmony_ci dev_err(smmu->dev, "embedded implementation not supported\n"); 357762306a36Sopenharmony_ci return -ENXIO; 357862306a36Sopenharmony_ci } 357962306a36Sopenharmony_ci 358062306a36Sopenharmony_ci /* Queue sizes, capped to ensure natural alignment */ 358162306a36Sopenharmony_ci smmu->cmdq.q.llq.max_n_shift = min_t(u32, CMDQ_MAX_SZ_SHIFT, 358262306a36Sopenharmony_ci FIELD_GET(IDR1_CMDQS, reg)); 358362306a36Sopenharmony_ci if (smmu->cmdq.q.llq.max_n_shift <= ilog2(CMDQ_BATCH_ENTRIES)) { 358462306a36Sopenharmony_ci /* 358562306a36Sopenharmony_ci * We don't support splitting up batches, so one batch of 358662306a36Sopenharmony_ci * commands plus an extra sync needs to fit inside the command 358762306a36Sopenharmony_ci * queue. There's also no way we can handle the weird alignment 358862306a36Sopenharmony_ci * restrictions on the base pointer for a unit-length queue. 358962306a36Sopenharmony_ci */ 359062306a36Sopenharmony_ci dev_err(smmu->dev, "command queue size <= %d entries not supported\n", 359162306a36Sopenharmony_ci CMDQ_BATCH_ENTRIES); 359262306a36Sopenharmony_ci return -ENXIO; 359362306a36Sopenharmony_ci } 359462306a36Sopenharmony_ci 359562306a36Sopenharmony_ci smmu->evtq.q.llq.max_n_shift = min_t(u32, EVTQ_MAX_SZ_SHIFT, 359662306a36Sopenharmony_ci FIELD_GET(IDR1_EVTQS, reg)); 359762306a36Sopenharmony_ci smmu->priq.q.llq.max_n_shift = min_t(u32, PRIQ_MAX_SZ_SHIFT, 359862306a36Sopenharmony_ci FIELD_GET(IDR1_PRIQS, reg)); 359962306a36Sopenharmony_ci 360062306a36Sopenharmony_ci /* SID/SSID sizes */ 360162306a36Sopenharmony_ci smmu->ssid_bits = FIELD_GET(IDR1_SSIDSIZE, reg); 360262306a36Sopenharmony_ci smmu->sid_bits = FIELD_GET(IDR1_SIDSIZE, reg); 360362306a36Sopenharmony_ci smmu->iommu.max_pasids = 1UL << smmu->ssid_bits; 360462306a36Sopenharmony_ci 360562306a36Sopenharmony_ci /* 360662306a36Sopenharmony_ci * If the SMMU supports fewer bits than would fill a single L2 stream 360762306a36Sopenharmony_ci * table, use a linear table instead. 360862306a36Sopenharmony_ci */ 360962306a36Sopenharmony_ci if (smmu->sid_bits <= STRTAB_SPLIT) 361062306a36Sopenharmony_ci smmu->features &= ~ARM_SMMU_FEAT_2_LVL_STRTAB; 361162306a36Sopenharmony_ci 361262306a36Sopenharmony_ci /* IDR3 */ 361362306a36Sopenharmony_ci reg = readl_relaxed(smmu->base + ARM_SMMU_IDR3); 361462306a36Sopenharmony_ci if (FIELD_GET(IDR3_RIL, reg)) 361562306a36Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_RANGE_INV; 361662306a36Sopenharmony_ci 361762306a36Sopenharmony_ci /* IDR5 */ 361862306a36Sopenharmony_ci reg = readl_relaxed(smmu->base + ARM_SMMU_IDR5); 361962306a36Sopenharmony_ci 362062306a36Sopenharmony_ci /* Maximum number of outstanding stalls */ 362162306a36Sopenharmony_ci smmu->evtq.max_stalls = FIELD_GET(IDR5_STALL_MAX, reg); 362262306a36Sopenharmony_ci 362362306a36Sopenharmony_ci /* Page sizes */ 362462306a36Sopenharmony_ci if (reg & IDR5_GRAN64K) 362562306a36Sopenharmony_ci smmu->pgsize_bitmap |= SZ_64K | SZ_512M; 362662306a36Sopenharmony_ci if (reg & IDR5_GRAN16K) 362762306a36Sopenharmony_ci smmu->pgsize_bitmap |= SZ_16K | SZ_32M; 362862306a36Sopenharmony_ci if (reg & IDR5_GRAN4K) 362962306a36Sopenharmony_ci smmu->pgsize_bitmap |= SZ_4K | SZ_2M | SZ_1G; 363062306a36Sopenharmony_ci 363162306a36Sopenharmony_ci /* Input address size */ 363262306a36Sopenharmony_ci if (FIELD_GET(IDR5_VAX, reg) == IDR5_VAX_52_BIT) 363362306a36Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_VAX; 363462306a36Sopenharmony_ci 363562306a36Sopenharmony_ci /* Output address size */ 363662306a36Sopenharmony_ci switch (FIELD_GET(IDR5_OAS, reg)) { 363762306a36Sopenharmony_ci case IDR5_OAS_32_BIT: 363862306a36Sopenharmony_ci smmu->oas = 32; 363962306a36Sopenharmony_ci break; 364062306a36Sopenharmony_ci case IDR5_OAS_36_BIT: 364162306a36Sopenharmony_ci smmu->oas = 36; 364262306a36Sopenharmony_ci break; 364362306a36Sopenharmony_ci case IDR5_OAS_40_BIT: 364462306a36Sopenharmony_ci smmu->oas = 40; 364562306a36Sopenharmony_ci break; 364662306a36Sopenharmony_ci case IDR5_OAS_42_BIT: 364762306a36Sopenharmony_ci smmu->oas = 42; 364862306a36Sopenharmony_ci break; 364962306a36Sopenharmony_ci case IDR5_OAS_44_BIT: 365062306a36Sopenharmony_ci smmu->oas = 44; 365162306a36Sopenharmony_ci break; 365262306a36Sopenharmony_ci case IDR5_OAS_52_BIT: 365362306a36Sopenharmony_ci smmu->oas = 52; 365462306a36Sopenharmony_ci smmu->pgsize_bitmap |= 1ULL << 42; /* 4TB */ 365562306a36Sopenharmony_ci break; 365662306a36Sopenharmony_ci default: 365762306a36Sopenharmony_ci dev_info(smmu->dev, 365862306a36Sopenharmony_ci "unknown output address size. Truncating to 48-bit\n"); 365962306a36Sopenharmony_ci fallthrough; 366062306a36Sopenharmony_ci case IDR5_OAS_48_BIT: 366162306a36Sopenharmony_ci smmu->oas = 48; 366262306a36Sopenharmony_ci } 366362306a36Sopenharmony_ci 366462306a36Sopenharmony_ci if (arm_smmu_ops.pgsize_bitmap == -1UL) 366562306a36Sopenharmony_ci arm_smmu_ops.pgsize_bitmap = smmu->pgsize_bitmap; 366662306a36Sopenharmony_ci else 366762306a36Sopenharmony_ci arm_smmu_ops.pgsize_bitmap |= smmu->pgsize_bitmap; 366862306a36Sopenharmony_ci 366962306a36Sopenharmony_ci /* Set the DMA mask for our table walker */ 367062306a36Sopenharmony_ci if (dma_set_mask_and_coherent(smmu->dev, DMA_BIT_MASK(smmu->oas))) 367162306a36Sopenharmony_ci dev_warn(smmu->dev, 367262306a36Sopenharmony_ci "failed to set DMA mask for table walker\n"); 367362306a36Sopenharmony_ci 367462306a36Sopenharmony_ci smmu->ias = max(smmu->ias, smmu->oas); 367562306a36Sopenharmony_ci 367662306a36Sopenharmony_ci if ((smmu->features & ARM_SMMU_FEAT_TRANS_S1) && 367762306a36Sopenharmony_ci (smmu->features & ARM_SMMU_FEAT_TRANS_S2)) 367862306a36Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_NESTING; 367962306a36Sopenharmony_ci 368062306a36Sopenharmony_ci arm_smmu_device_iidr_probe(smmu); 368162306a36Sopenharmony_ci 368262306a36Sopenharmony_ci if (arm_smmu_sva_supported(smmu)) 368362306a36Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_SVA; 368462306a36Sopenharmony_ci 368562306a36Sopenharmony_ci dev_info(smmu->dev, "ias %lu-bit, oas %lu-bit (features 0x%08x)\n", 368662306a36Sopenharmony_ci smmu->ias, smmu->oas, smmu->features); 368762306a36Sopenharmony_ci return 0; 368862306a36Sopenharmony_ci} 368962306a36Sopenharmony_ci 369062306a36Sopenharmony_ci#ifdef CONFIG_ACPI 369162306a36Sopenharmony_cistatic void acpi_smmu_get_options(u32 model, struct arm_smmu_device *smmu) 369262306a36Sopenharmony_ci{ 369362306a36Sopenharmony_ci switch (model) { 369462306a36Sopenharmony_ci case ACPI_IORT_SMMU_V3_CAVIUM_CN99XX: 369562306a36Sopenharmony_ci smmu->options |= ARM_SMMU_OPT_PAGE0_REGS_ONLY; 369662306a36Sopenharmony_ci break; 369762306a36Sopenharmony_ci case ACPI_IORT_SMMU_V3_HISILICON_HI161X: 369862306a36Sopenharmony_ci smmu->options |= ARM_SMMU_OPT_SKIP_PREFETCH; 369962306a36Sopenharmony_ci break; 370062306a36Sopenharmony_ci } 370162306a36Sopenharmony_ci 370262306a36Sopenharmony_ci dev_notice(smmu->dev, "option mask 0x%x\n", smmu->options); 370362306a36Sopenharmony_ci} 370462306a36Sopenharmony_ci 370562306a36Sopenharmony_cistatic int arm_smmu_device_acpi_probe(struct platform_device *pdev, 370662306a36Sopenharmony_ci struct arm_smmu_device *smmu) 370762306a36Sopenharmony_ci{ 370862306a36Sopenharmony_ci struct acpi_iort_smmu_v3 *iort_smmu; 370962306a36Sopenharmony_ci struct device *dev = smmu->dev; 371062306a36Sopenharmony_ci struct acpi_iort_node *node; 371162306a36Sopenharmony_ci 371262306a36Sopenharmony_ci node = *(struct acpi_iort_node **)dev_get_platdata(dev); 371362306a36Sopenharmony_ci 371462306a36Sopenharmony_ci /* Retrieve SMMUv3 specific data */ 371562306a36Sopenharmony_ci iort_smmu = (struct acpi_iort_smmu_v3 *)node->node_data; 371662306a36Sopenharmony_ci 371762306a36Sopenharmony_ci acpi_smmu_get_options(iort_smmu->model, smmu); 371862306a36Sopenharmony_ci 371962306a36Sopenharmony_ci if (iort_smmu->flags & ACPI_IORT_SMMU_V3_COHACC_OVERRIDE) 372062306a36Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_COHERENCY; 372162306a36Sopenharmony_ci 372262306a36Sopenharmony_ci return 0; 372362306a36Sopenharmony_ci} 372462306a36Sopenharmony_ci#else 372562306a36Sopenharmony_cistatic inline int arm_smmu_device_acpi_probe(struct platform_device *pdev, 372662306a36Sopenharmony_ci struct arm_smmu_device *smmu) 372762306a36Sopenharmony_ci{ 372862306a36Sopenharmony_ci return -ENODEV; 372962306a36Sopenharmony_ci} 373062306a36Sopenharmony_ci#endif 373162306a36Sopenharmony_ci 373262306a36Sopenharmony_cistatic int arm_smmu_device_dt_probe(struct platform_device *pdev, 373362306a36Sopenharmony_ci struct arm_smmu_device *smmu) 373462306a36Sopenharmony_ci{ 373562306a36Sopenharmony_ci struct device *dev = &pdev->dev; 373662306a36Sopenharmony_ci u32 cells; 373762306a36Sopenharmony_ci int ret = -EINVAL; 373862306a36Sopenharmony_ci 373962306a36Sopenharmony_ci if (of_property_read_u32(dev->of_node, "#iommu-cells", &cells)) 374062306a36Sopenharmony_ci dev_err(dev, "missing #iommu-cells property\n"); 374162306a36Sopenharmony_ci else if (cells != 1) 374262306a36Sopenharmony_ci dev_err(dev, "invalid #iommu-cells value (%d)\n", cells); 374362306a36Sopenharmony_ci else 374462306a36Sopenharmony_ci ret = 0; 374562306a36Sopenharmony_ci 374662306a36Sopenharmony_ci parse_driver_options(smmu); 374762306a36Sopenharmony_ci 374862306a36Sopenharmony_ci if (of_dma_is_coherent(dev->of_node)) 374962306a36Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_COHERENCY; 375062306a36Sopenharmony_ci 375162306a36Sopenharmony_ci return ret; 375262306a36Sopenharmony_ci} 375362306a36Sopenharmony_ci 375462306a36Sopenharmony_cistatic unsigned long arm_smmu_resource_size(struct arm_smmu_device *smmu) 375562306a36Sopenharmony_ci{ 375662306a36Sopenharmony_ci if (smmu->options & ARM_SMMU_OPT_PAGE0_REGS_ONLY) 375762306a36Sopenharmony_ci return SZ_64K; 375862306a36Sopenharmony_ci else 375962306a36Sopenharmony_ci return SZ_128K; 376062306a36Sopenharmony_ci} 376162306a36Sopenharmony_ci 376262306a36Sopenharmony_cistatic void __iomem *arm_smmu_ioremap(struct device *dev, resource_size_t start, 376362306a36Sopenharmony_ci resource_size_t size) 376462306a36Sopenharmony_ci{ 376562306a36Sopenharmony_ci struct resource res = DEFINE_RES_MEM(start, size); 376662306a36Sopenharmony_ci 376762306a36Sopenharmony_ci return devm_ioremap_resource(dev, &res); 376862306a36Sopenharmony_ci} 376962306a36Sopenharmony_ci 377062306a36Sopenharmony_cistatic void arm_smmu_rmr_install_bypass_ste(struct arm_smmu_device *smmu) 377162306a36Sopenharmony_ci{ 377262306a36Sopenharmony_ci struct list_head rmr_list; 377362306a36Sopenharmony_ci struct iommu_resv_region *e; 377462306a36Sopenharmony_ci 377562306a36Sopenharmony_ci INIT_LIST_HEAD(&rmr_list); 377662306a36Sopenharmony_ci iort_get_rmr_sids(dev_fwnode(smmu->dev), &rmr_list); 377762306a36Sopenharmony_ci 377862306a36Sopenharmony_ci list_for_each_entry(e, &rmr_list, list) { 377962306a36Sopenharmony_ci __le64 *step; 378062306a36Sopenharmony_ci struct iommu_iort_rmr_data *rmr; 378162306a36Sopenharmony_ci int ret, i; 378262306a36Sopenharmony_ci 378362306a36Sopenharmony_ci rmr = container_of(e, struct iommu_iort_rmr_data, rr); 378462306a36Sopenharmony_ci for (i = 0; i < rmr->num_sids; i++) { 378562306a36Sopenharmony_ci ret = arm_smmu_init_sid_strtab(smmu, rmr->sids[i]); 378662306a36Sopenharmony_ci if (ret) { 378762306a36Sopenharmony_ci dev_err(smmu->dev, "RMR SID(0x%x) bypass failed\n", 378862306a36Sopenharmony_ci rmr->sids[i]); 378962306a36Sopenharmony_ci continue; 379062306a36Sopenharmony_ci } 379162306a36Sopenharmony_ci 379262306a36Sopenharmony_ci step = arm_smmu_get_step_for_sid(smmu, rmr->sids[i]); 379362306a36Sopenharmony_ci arm_smmu_init_bypass_stes(step, 1, true); 379462306a36Sopenharmony_ci } 379562306a36Sopenharmony_ci } 379662306a36Sopenharmony_ci 379762306a36Sopenharmony_ci iort_put_rmr_sids(dev_fwnode(smmu->dev), &rmr_list); 379862306a36Sopenharmony_ci} 379962306a36Sopenharmony_ci 380062306a36Sopenharmony_cistatic int arm_smmu_device_probe(struct platform_device *pdev) 380162306a36Sopenharmony_ci{ 380262306a36Sopenharmony_ci int irq, ret; 380362306a36Sopenharmony_ci struct resource *res; 380462306a36Sopenharmony_ci resource_size_t ioaddr; 380562306a36Sopenharmony_ci struct arm_smmu_device *smmu; 380662306a36Sopenharmony_ci struct device *dev = &pdev->dev; 380762306a36Sopenharmony_ci bool bypass; 380862306a36Sopenharmony_ci 380962306a36Sopenharmony_ci smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL); 381062306a36Sopenharmony_ci if (!smmu) 381162306a36Sopenharmony_ci return -ENOMEM; 381262306a36Sopenharmony_ci smmu->dev = dev; 381362306a36Sopenharmony_ci 381462306a36Sopenharmony_ci if (dev->of_node) { 381562306a36Sopenharmony_ci ret = arm_smmu_device_dt_probe(pdev, smmu); 381662306a36Sopenharmony_ci } else { 381762306a36Sopenharmony_ci ret = arm_smmu_device_acpi_probe(pdev, smmu); 381862306a36Sopenharmony_ci if (ret == -ENODEV) 381962306a36Sopenharmony_ci return ret; 382062306a36Sopenharmony_ci } 382162306a36Sopenharmony_ci 382262306a36Sopenharmony_ci /* Set bypass mode according to firmware probing result */ 382362306a36Sopenharmony_ci bypass = !!ret; 382462306a36Sopenharmony_ci 382562306a36Sopenharmony_ci /* Base address */ 382662306a36Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 382762306a36Sopenharmony_ci if (!res) 382862306a36Sopenharmony_ci return -EINVAL; 382962306a36Sopenharmony_ci if (resource_size(res) < arm_smmu_resource_size(smmu)) { 383062306a36Sopenharmony_ci dev_err(dev, "MMIO region too small (%pr)\n", res); 383162306a36Sopenharmony_ci return -EINVAL; 383262306a36Sopenharmony_ci } 383362306a36Sopenharmony_ci ioaddr = res->start; 383462306a36Sopenharmony_ci 383562306a36Sopenharmony_ci /* 383662306a36Sopenharmony_ci * Don't map the IMPLEMENTATION DEFINED regions, since they may contain 383762306a36Sopenharmony_ci * the PMCG registers which are reserved by the PMU driver. 383862306a36Sopenharmony_ci */ 383962306a36Sopenharmony_ci smmu->base = arm_smmu_ioremap(dev, ioaddr, ARM_SMMU_REG_SZ); 384062306a36Sopenharmony_ci if (IS_ERR(smmu->base)) 384162306a36Sopenharmony_ci return PTR_ERR(smmu->base); 384262306a36Sopenharmony_ci 384362306a36Sopenharmony_ci if (arm_smmu_resource_size(smmu) > SZ_64K) { 384462306a36Sopenharmony_ci smmu->page1 = arm_smmu_ioremap(dev, ioaddr + SZ_64K, 384562306a36Sopenharmony_ci ARM_SMMU_REG_SZ); 384662306a36Sopenharmony_ci if (IS_ERR(smmu->page1)) 384762306a36Sopenharmony_ci return PTR_ERR(smmu->page1); 384862306a36Sopenharmony_ci } else { 384962306a36Sopenharmony_ci smmu->page1 = smmu->base; 385062306a36Sopenharmony_ci } 385162306a36Sopenharmony_ci 385262306a36Sopenharmony_ci /* Interrupt lines */ 385362306a36Sopenharmony_ci 385462306a36Sopenharmony_ci irq = platform_get_irq_byname_optional(pdev, "combined"); 385562306a36Sopenharmony_ci if (irq > 0) 385662306a36Sopenharmony_ci smmu->combined_irq = irq; 385762306a36Sopenharmony_ci else { 385862306a36Sopenharmony_ci irq = platform_get_irq_byname_optional(pdev, "eventq"); 385962306a36Sopenharmony_ci if (irq > 0) 386062306a36Sopenharmony_ci smmu->evtq.q.irq = irq; 386162306a36Sopenharmony_ci 386262306a36Sopenharmony_ci irq = platform_get_irq_byname_optional(pdev, "priq"); 386362306a36Sopenharmony_ci if (irq > 0) 386462306a36Sopenharmony_ci smmu->priq.q.irq = irq; 386562306a36Sopenharmony_ci 386662306a36Sopenharmony_ci irq = platform_get_irq_byname_optional(pdev, "gerror"); 386762306a36Sopenharmony_ci if (irq > 0) 386862306a36Sopenharmony_ci smmu->gerr_irq = irq; 386962306a36Sopenharmony_ci } 387062306a36Sopenharmony_ci /* Probe the h/w */ 387162306a36Sopenharmony_ci ret = arm_smmu_device_hw_probe(smmu); 387262306a36Sopenharmony_ci if (ret) 387362306a36Sopenharmony_ci return ret; 387462306a36Sopenharmony_ci 387562306a36Sopenharmony_ci /* Initialise in-memory data structures */ 387662306a36Sopenharmony_ci ret = arm_smmu_init_structures(smmu); 387762306a36Sopenharmony_ci if (ret) 387862306a36Sopenharmony_ci return ret; 387962306a36Sopenharmony_ci 388062306a36Sopenharmony_ci /* Record our private device structure */ 388162306a36Sopenharmony_ci platform_set_drvdata(pdev, smmu); 388262306a36Sopenharmony_ci 388362306a36Sopenharmony_ci /* Check for RMRs and install bypass STEs if any */ 388462306a36Sopenharmony_ci arm_smmu_rmr_install_bypass_ste(smmu); 388562306a36Sopenharmony_ci 388662306a36Sopenharmony_ci /* Reset the device */ 388762306a36Sopenharmony_ci ret = arm_smmu_device_reset(smmu, bypass); 388862306a36Sopenharmony_ci if (ret) 388962306a36Sopenharmony_ci return ret; 389062306a36Sopenharmony_ci 389162306a36Sopenharmony_ci /* And we're up. Go go go! */ 389262306a36Sopenharmony_ci ret = iommu_device_sysfs_add(&smmu->iommu, dev, NULL, 389362306a36Sopenharmony_ci "smmu3.%pa", &ioaddr); 389462306a36Sopenharmony_ci if (ret) 389562306a36Sopenharmony_ci return ret; 389662306a36Sopenharmony_ci 389762306a36Sopenharmony_ci ret = iommu_device_register(&smmu->iommu, &arm_smmu_ops, dev); 389862306a36Sopenharmony_ci if (ret) { 389962306a36Sopenharmony_ci dev_err(dev, "Failed to register iommu\n"); 390062306a36Sopenharmony_ci iommu_device_sysfs_remove(&smmu->iommu); 390162306a36Sopenharmony_ci return ret; 390262306a36Sopenharmony_ci } 390362306a36Sopenharmony_ci 390462306a36Sopenharmony_ci return 0; 390562306a36Sopenharmony_ci} 390662306a36Sopenharmony_ci 390762306a36Sopenharmony_cistatic void arm_smmu_device_remove(struct platform_device *pdev) 390862306a36Sopenharmony_ci{ 390962306a36Sopenharmony_ci struct arm_smmu_device *smmu = platform_get_drvdata(pdev); 391062306a36Sopenharmony_ci 391162306a36Sopenharmony_ci iommu_device_unregister(&smmu->iommu); 391262306a36Sopenharmony_ci iommu_device_sysfs_remove(&smmu->iommu); 391362306a36Sopenharmony_ci arm_smmu_device_disable(smmu); 391462306a36Sopenharmony_ci iopf_queue_free(smmu->evtq.iopf); 391562306a36Sopenharmony_ci ida_destroy(&smmu->vmid_map); 391662306a36Sopenharmony_ci} 391762306a36Sopenharmony_ci 391862306a36Sopenharmony_cistatic void arm_smmu_device_shutdown(struct platform_device *pdev) 391962306a36Sopenharmony_ci{ 392062306a36Sopenharmony_ci struct arm_smmu_device *smmu = platform_get_drvdata(pdev); 392162306a36Sopenharmony_ci 392262306a36Sopenharmony_ci arm_smmu_device_disable(smmu); 392362306a36Sopenharmony_ci} 392462306a36Sopenharmony_ci 392562306a36Sopenharmony_cistatic const struct of_device_id arm_smmu_of_match[] = { 392662306a36Sopenharmony_ci { .compatible = "arm,smmu-v3", }, 392762306a36Sopenharmony_ci { }, 392862306a36Sopenharmony_ci}; 392962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, arm_smmu_of_match); 393062306a36Sopenharmony_ci 393162306a36Sopenharmony_cistatic void arm_smmu_driver_unregister(struct platform_driver *drv) 393262306a36Sopenharmony_ci{ 393362306a36Sopenharmony_ci arm_smmu_sva_notifier_synchronize(); 393462306a36Sopenharmony_ci platform_driver_unregister(drv); 393562306a36Sopenharmony_ci} 393662306a36Sopenharmony_ci 393762306a36Sopenharmony_cistatic struct platform_driver arm_smmu_driver = { 393862306a36Sopenharmony_ci .driver = { 393962306a36Sopenharmony_ci .name = "arm-smmu-v3", 394062306a36Sopenharmony_ci .of_match_table = arm_smmu_of_match, 394162306a36Sopenharmony_ci .suppress_bind_attrs = true, 394262306a36Sopenharmony_ci }, 394362306a36Sopenharmony_ci .probe = arm_smmu_device_probe, 394462306a36Sopenharmony_ci .remove_new = arm_smmu_device_remove, 394562306a36Sopenharmony_ci .shutdown = arm_smmu_device_shutdown, 394662306a36Sopenharmony_ci}; 394762306a36Sopenharmony_cimodule_driver(arm_smmu_driver, platform_driver_register, 394862306a36Sopenharmony_ci arm_smmu_driver_unregister); 394962306a36Sopenharmony_ci 395062306a36Sopenharmony_ciMODULE_DESCRIPTION("IOMMU API for ARM architected SMMUv3 implementations"); 395162306a36Sopenharmony_ciMODULE_AUTHOR("Will Deacon <will@kernel.org>"); 395262306a36Sopenharmony_ciMODULE_ALIAS("platform:arm-smmu-v3"); 395362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 3954