18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * IOMMU API for ARM architected SMMUv3 implementations. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2015 ARM Limited 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author: Will Deacon <will.deacon@arm.com> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * This driver is powered by bad coffee and bombay mix. 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/acpi.h> 138c2ecf20Sopenharmony_ci#include <linux/acpi_iort.h> 148c2ecf20Sopenharmony_ci#include <linux/bitops.h> 158c2ecf20Sopenharmony_ci#include <linux/crash_dump.h> 168c2ecf20Sopenharmony_ci#include <linux/delay.h> 178c2ecf20Sopenharmony_ci#include <linux/dma-iommu.h> 188c2ecf20Sopenharmony_ci#include <linux/err.h> 198c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 208c2ecf20Sopenharmony_ci#include <linux/io-pgtable.h> 218c2ecf20Sopenharmony_ci#include <linux/iopoll.h> 228c2ecf20Sopenharmony_ci#include <linux/module.h> 238c2ecf20Sopenharmony_ci#include <linux/msi.h> 248c2ecf20Sopenharmony_ci#include <linux/of.h> 258c2ecf20Sopenharmony_ci#include <linux/of_address.h> 268c2ecf20Sopenharmony_ci#include <linux/of_iommu.h> 278c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 288c2ecf20Sopenharmony_ci#include <linux/pci.h> 298c2ecf20Sopenharmony_ci#include <linux/pci-ats.h> 308c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#include <linux/amba/bus.h> 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#include "arm-smmu-v3.h" 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic bool disable_bypass = 1; 378c2ecf20Sopenharmony_cimodule_param(disable_bypass, bool, 0444); 388c2ecf20Sopenharmony_ciMODULE_PARM_DESC(disable_bypass, 398c2ecf20Sopenharmony_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."); 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic bool disable_msipolling; 428c2ecf20Sopenharmony_cimodule_param(disable_msipolling, bool, 0444); 438c2ecf20Sopenharmony_ciMODULE_PARM_DESC(disable_msipolling, 448c2ecf20Sopenharmony_ci "Disable MSI-based polling for CMD_SYNC completion."); 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cienum arm_smmu_msi_index { 478c2ecf20Sopenharmony_ci EVTQ_MSI_INDEX, 488c2ecf20Sopenharmony_ci GERROR_MSI_INDEX, 498c2ecf20Sopenharmony_ci PRIQ_MSI_INDEX, 508c2ecf20Sopenharmony_ci ARM_SMMU_MAX_MSIS, 518c2ecf20Sopenharmony_ci}; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic phys_addr_t arm_smmu_msi_cfg[ARM_SMMU_MAX_MSIS][3] = { 548c2ecf20Sopenharmony_ci [EVTQ_MSI_INDEX] = { 558c2ecf20Sopenharmony_ci ARM_SMMU_EVTQ_IRQ_CFG0, 568c2ecf20Sopenharmony_ci ARM_SMMU_EVTQ_IRQ_CFG1, 578c2ecf20Sopenharmony_ci ARM_SMMU_EVTQ_IRQ_CFG2, 588c2ecf20Sopenharmony_ci }, 598c2ecf20Sopenharmony_ci [GERROR_MSI_INDEX] = { 608c2ecf20Sopenharmony_ci ARM_SMMU_GERROR_IRQ_CFG0, 618c2ecf20Sopenharmony_ci ARM_SMMU_GERROR_IRQ_CFG1, 628c2ecf20Sopenharmony_ci ARM_SMMU_GERROR_IRQ_CFG2, 638c2ecf20Sopenharmony_ci }, 648c2ecf20Sopenharmony_ci [PRIQ_MSI_INDEX] = { 658c2ecf20Sopenharmony_ci ARM_SMMU_PRIQ_IRQ_CFG0, 668c2ecf20Sopenharmony_ci ARM_SMMU_PRIQ_IRQ_CFG1, 678c2ecf20Sopenharmony_ci ARM_SMMU_PRIQ_IRQ_CFG2, 688c2ecf20Sopenharmony_ci }, 698c2ecf20Sopenharmony_ci}; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistruct arm_smmu_option_prop { 728c2ecf20Sopenharmony_ci u32 opt; 738c2ecf20Sopenharmony_ci const char *prop; 748c2ecf20Sopenharmony_ci}; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ciDEFINE_XARRAY_ALLOC1(arm_smmu_asid_xa); 778c2ecf20Sopenharmony_ciDEFINE_MUTEX(arm_smmu_asid_lock); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic struct arm_smmu_option_prop arm_smmu_options[] = { 808c2ecf20Sopenharmony_ci { ARM_SMMU_OPT_SKIP_PREFETCH, "hisilicon,broken-prefetch-cmd" }, 818c2ecf20Sopenharmony_ci { ARM_SMMU_OPT_PAGE0_REGS_ONLY, "cavium,cn9900-broken-page1-regspace"}, 828c2ecf20Sopenharmony_ci { 0, NULL}, 838c2ecf20Sopenharmony_ci}; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic inline void __iomem *arm_smmu_page1_fixup(unsigned long offset, 868c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci if (offset > SZ_64K) 898c2ecf20Sopenharmony_ci return smmu->page1 + offset - SZ_64K; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci return smmu->base + offset; 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic struct arm_smmu_domain *to_smmu_domain(struct iommu_domain *dom) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci return container_of(dom, struct arm_smmu_domain, domain); 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic void parse_driver_options(struct arm_smmu_device *smmu) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci int i = 0; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci do { 1048c2ecf20Sopenharmony_ci if (of_property_read_bool(smmu->dev->of_node, 1058c2ecf20Sopenharmony_ci arm_smmu_options[i].prop)) { 1068c2ecf20Sopenharmony_ci smmu->options |= arm_smmu_options[i].opt; 1078c2ecf20Sopenharmony_ci dev_notice(smmu->dev, "option %s\n", 1088c2ecf20Sopenharmony_ci arm_smmu_options[i].prop); 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci } while (arm_smmu_options[++i].opt); 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci/* Low-level queue manipulation functions */ 1148c2ecf20Sopenharmony_cistatic bool queue_has_space(struct arm_smmu_ll_queue *q, u32 n) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci u32 space, prod, cons; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci prod = Q_IDX(q, q->prod); 1198c2ecf20Sopenharmony_ci cons = Q_IDX(q, q->cons); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci if (Q_WRP(q, q->prod) == Q_WRP(q, q->cons)) 1228c2ecf20Sopenharmony_ci space = (1 << q->max_n_shift) - (prod - cons); 1238c2ecf20Sopenharmony_ci else 1248c2ecf20Sopenharmony_ci space = cons - prod; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci return space >= n; 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic bool queue_full(struct arm_smmu_ll_queue *q) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci return Q_IDX(q, q->prod) == Q_IDX(q, q->cons) && 1328c2ecf20Sopenharmony_ci Q_WRP(q, q->prod) != Q_WRP(q, q->cons); 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic bool queue_empty(struct arm_smmu_ll_queue *q) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci return Q_IDX(q, q->prod) == Q_IDX(q, q->cons) && 1388c2ecf20Sopenharmony_ci Q_WRP(q, q->prod) == Q_WRP(q, q->cons); 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic bool queue_consumed(struct arm_smmu_ll_queue *q, u32 prod) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci return ((Q_WRP(q, q->cons) == Q_WRP(q, prod)) && 1448c2ecf20Sopenharmony_ci (Q_IDX(q, q->cons) > Q_IDX(q, prod))) || 1458c2ecf20Sopenharmony_ci ((Q_WRP(q, q->cons) != Q_WRP(q, prod)) && 1468c2ecf20Sopenharmony_ci (Q_IDX(q, q->cons) <= Q_IDX(q, prod))); 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic void queue_sync_cons_out(struct arm_smmu_queue *q) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci /* 1528c2ecf20Sopenharmony_ci * Ensure that all CPU accesses (reads and writes) to the queue 1538c2ecf20Sopenharmony_ci * are complete before we update the cons pointer. 1548c2ecf20Sopenharmony_ci */ 1558c2ecf20Sopenharmony_ci __iomb(); 1568c2ecf20Sopenharmony_ci writel_relaxed(q->llq.cons, q->cons_reg); 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic void queue_inc_cons(struct arm_smmu_ll_queue *q) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci u32 cons = (Q_WRP(q, q->cons) | Q_IDX(q, q->cons)) + 1; 1628c2ecf20Sopenharmony_ci q->cons = Q_OVF(q->cons) | Q_WRP(q, cons) | Q_IDX(q, cons); 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic void queue_sync_cons_ovf(struct arm_smmu_queue *q) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci struct arm_smmu_ll_queue *llq = &q->llq; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci if (likely(Q_OVF(llq->prod) == Q_OVF(llq->cons))) 1708c2ecf20Sopenharmony_ci return; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci llq->cons = Q_OVF(llq->prod) | Q_WRP(llq, llq->cons) | 1738c2ecf20Sopenharmony_ci Q_IDX(llq, llq->cons); 1748c2ecf20Sopenharmony_ci queue_sync_cons_out(q); 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic int queue_sync_prod_in(struct arm_smmu_queue *q) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci u32 prod; 1808c2ecf20Sopenharmony_ci int ret = 0; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci /* 1838c2ecf20Sopenharmony_ci * We can't use the _relaxed() variant here, as we must prevent 1848c2ecf20Sopenharmony_ci * speculative reads of the queue before we have determined that 1858c2ecf20Sopenharmony_ci * prod has indeed moved. 1868c2ecf20Sopenharmony_ci */ 1878c2ecf20Sopenharmony_ci prod = readl(q->prod_reg); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci if (Q_OVF(prod) != Q_OVF(q->llq.prod)) 1908c2ecf20Sopenharmony_ci ret = -EOVERFLOW; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci q->llq.prod = prod; 1938c2ecf20Sopenharmony_ci return ret; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic u32 queue_inc_prod_n(struct arm_smmu_ll_queue *q, int n) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci u32 prod = (Q_WRP(q, q->prod) | Q_IDX(q, q->prod)) + n; 1998c2ecf20Sopenharmony_ci return Q_OVF(q->prod) | Q_WRP(q, prod) | Q_IDX(q, prod); 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistatic void queue_poll_init(struct arm_smmu_device *smmu, 2038c2ecf20Sopenharmony_ci struct arm_smmu_queue_poll *qp) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci qp->delay = 1; 2068c2ecf20Sopenharmony_ci qp->spin_cnt = 0; 2078c2ecf20Sopenharmony_ci qp->wfe = !!(smmu->features & ARM_SMMU_FEAT_SEV); 2088c2ecf20Sopenharmony_ci qp->timeout = ktime_add_us(ktime_get(), ARM_SMMU_POLL_TIMEOUT_US); 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic int queue_poll(struct arm_smmu_queue_poll *qp) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci if (ktime_compare(ktime_get(), qp->timeout) > 0) 2148c2ecf20Sopenharmony_ci return -ETIMEDOUT; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci if (qp->wfe) { 2178c2ecf20Sopenharmony_ci wfe(); 2188c2ecf20Sopenharmony_ci } else if (++qp->spin_cnt < ARM_SMMU_POLL_SPIN_COUNT) { 2198c2ecf20Sopenharmony_ci cpu_relax(); 2208c2ecf20Sopenharmony_ci } else { 2218c2ecf20Sopenharmony_ci udelay(qp->delay); 2228c2ecf20Sopenharmony_ci qp->delay *= 2; 2238c2ecf20Sopenharmony_ci qp->spin_cnt = 0; 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci return 0; 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic void queue_write(__le64 *dst, u64 *src, size_t n_dwords) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci int i; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci for (i = 0; i < n_dwords; ++i) 2348c2ecf20Sopenharmony_ci *dst++ = cpu_to_le64(*src++); 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic void queue_read(u64 *dst, __le64 *src, size_t n_dwords) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci int i; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci for (i = 0; i < n_dwords; ++i) 2428c2ecf20Sopenharmony_ci *dst++ = le64_to_cpu(*src++); 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic int queue_remove_raw(struct arm_smmu_queue *q, u64 *ent) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci if (queue_empty(&q->llq)) 2488c2ecf20Sopenharmony_ci return -EAGAIN; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci queue_read(ent, Q_ENT(q, q->llq.cons), q->ent_dwords); 2518c2ecf20Sopenharmony_ci queue_inc_cons(&q->llq); 2528c2ecf20Sopenharmony_ci queue_sync_cons_out(q); 2538c2ecf20Sopenharmony_ci return 0; 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci/* High-level queue accessors */ 2578c2ecf20Sopenharmony_cistatic int arm_smmu_cmdq_build_cmd(u64 *cmd, struct arm_smmu_cmdq_ent *ent) 2588c2ecf20Sopenharmony_ci{ 2598c2ecf20Sopenharmony_ci memset(cmd, 0, 1 << CMDQ_ENT_SZ_SHIFT); 2608c2ecf20Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_0_OP, ent->opcode); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci switch (ent->opcode) { 2638c2ecf20Sopenharmony_ci case CMDQ_OP_TLBI_EL2_ALL: 2648c2ecf20Sopenharmony_ci case CMDQ_OP_TLBI_NSNH_ALL: 2658c2ecf20Sopenharmony_ci break; 2668c2ecf20Sopenharmony_ci case CMDQ_OP_PREFETCH_CFG: 2678c2ecf20Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_PREFETCH_0_SID, ent->prefetch.sid); 2688c2ecf20Sopenharmony_ci cmd[1] |= FIELD_PREP(CMDQ_PREFETCH_1_SIZE, ent->prefetch.size); 2698c2ecf20Sopenharmony_ci cmd[1] |= ent->prefetch.addr & CMDQ_PREFETCH_1_ADDR_MASK; 2708c2ecf20Sopenharmony_ci break; 2718c2ecf20Sopenharmony_ci case CMDQ_OP_CFGI_CD: 2728c2ecf20Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_CFGI_0_SSID, ent->cfgi.ssid); 2738c2ecf20Sopenharmony_ci fallthrough; 2748c2ecf20Sopenharmony_ci case CMDQ_OP_CFGI_STE: 2758c2ecf20Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_CFGI_0_SID, ent->cfgi.sid); 2768c2ecf20Sopenharmony_ci cmd[1] |= FIELD_PREP(CMDQ_CFGI_1_LEAF, ent->cfgi.leaf); 2778c2ecf20Sopenharmony_ci break; 2788c2ecf20Sopenharmony_ci case CMDQ_OP_CFGI_CD_ALL: 2798c2ecf20Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_CFGI_0_SID, ent->cfgi.sid); 2808c2ecf20Sopenharmony_ci break; 2818c2ecf20Sopenharmony_ci case CMDQ_OP_CFGI_ALL: 2828c2ecf20Sopenharmony_ci /* Cover the entire SID range */ 2838c2ecf20Sopenharmony_ci cmd[1] |= FIELD_PREP(CMDQ_CFGI_1_RANGE, 31); 2848c2ecf20Sopenharmony_ci break; 2858c2ecf20Sopenharmony_ci case CMDQ_OP_TLBI_NH_VA: 2868c2ecf20Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_NUM, ent->tlbi.num); 2878c2ecf20Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_SCALE, ent->tlbi.scale); 2888c2ecf20Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_VMID, ent->tlbi.vmid); 2898c2ecf20Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_ASID, ent->tlbi.asid); 2908c2ecf20Sopenharmony_ci cmd[1] |= FIELD_PREP(CMDQ_TLBI_1_LEAF, ent->tlbi.leaf); 2918c2ecf20Sopenharmony_ci cmd[1] |= FIELD_PREP(CMDQ_TLBI_1_TTL, ent->tlbi.ttl); 2928c2ecf20Sopenharmony_ci cmd[1] |= FIELD_PREP(CMDQ_TLBI_1_TG, ent->tlbi.tg); 2938c2ecf20Sopenharmony_ci cmd[1] |= ent->tlbi.addr & CMDQ_TLBI_1_VA_MASK; 2948c2ecf20Sopenharmony_ci break; 2958c2ecf20Sopenharmony_ci case CMDQ_OP_TLBI_S2_IPA: 2968c2ecf20Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_NUM, ent->tlbi.num); 2978c2ecf20Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_SCALE, ent->tlbi.scale); 2988c2ecf20Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_VMID, ent->tlbi.vmid); 2998c2ecf20Sopenharmony_ci cmd[1] |= FIELD_PREP(CMDQ_TLBI_1_LEAF, ent->tlbi.leaf); 3008c2ecf20Sopenharmony_ci cmd[1] |= FIELD_PREP(CMDQ_TLBI_1_TTL, ent->tlbi.ttl); 3018c2ecf20Sopenharmony_ci cmd[1] |= FIELD_PREP(CMDQ_TLBI_1_TG, ent->tlbi.tg); 3028c2ecf20Sopenharmony_ci cmd[1] |= ent->tlbi.addr & CMDQ_TLBI_1_IPA_MASK; 3038c2ecf20Sopenharmony_ci break; 3048c2ecf20Sopenharmony_ci case CMDQ_OP_TLBI_NH_ASID: 3058c2ecf20Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_ASID, ent->tlbi.asid); 3068c2ecf20Sopenharmony_ci fallthrough; 3078c2ecf20Sopenharmony_ci case CMDQ_OP_TLBI_S12_VMALL: 3088c2ecf20Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_TLBI_0_VMID, ent->tlbi.vmid); 3098c2ecf20Sopenharmony_ci break; 3108c2ecf20Sopenharmony_ci case CMDQ_OP_ATC_INV: 3118c2ecf20Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_0_SSV, ent->substream_valid); 3128c2ecf20Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_ATC_0_GLOBAL, ent->atc.global); 3138c2ecf20Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_ATC_0_SSID, ent->atc.ssid); 3148c2ecf20Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_ATC_0_SID, ent->atc.sid); 3158c2ecf20Sopenharmony_ci cmd[1] |= FIELD_PREP(CMDQ_ATC_1_SIZE, ent->atc.size); 3168c2ecf20Sopenharmony_ci cmd[1] |= ent->atc.addr & CMDQ_ATC_1_ADDR_MASK; 3178c2ecf20Sopenharmony_ci break; 3188c2ecf20Sopenharmony_ci case CMDQ_OP_PRI_RESP: 3198c2ecf20Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_0_SSV, ent->substream_valid); 3208c2ecf20Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_PRI_0_SSID, ent->pri.ssid); 3218c2ecf20Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_PRI_0_SID, ent->pri.sid); 3228c2ecf20Sopenharmony_ci cmd[1] |= FIELD_PREP(CMDQ_PRI_1_GRPID, ent->pri.grpid); 3238c2ecf20Sopenharmony_ci switch (ent->pri.resp) { 3248c2ecf20Sopenharmony_ci case PRI_RESP_DENY: 3258c2ecf20Sopenharmony_ci case PRI_RESP_FAIL: 3268c2ecf20Sopenharmony_ci case PRI_RESP_SUCC: 3278c2ecf20Sopenharmony_ci break; 3288c2ecf20Sopenharmony_ci default: 3298c2ecf20Sopenharmony_ci return -EINVAL; 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci cmd[1] |= FIELD_PREP(CMDQ_PRI_1_RESP, ent->pri.resp); 3328c2ecf20Sopenharmony_ci break; 3338c2ecf20Sopenharmony_ci case CMDQ_OP_CMD_SYNC: 3348c2ecf20Sopenharmony_ci if (ent->sync.msiaddr) { 3358c2ecf20Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_SYNC_0_CS, CMDQ_SYNC_0_CS_IRQ); 3368c2ecf20Sopenharmony_ci cmd[1] |= ent->sync.msiaddr & CMDQ_SYNC_1_MSIADDR_MASK; 3378c2ecf20Sopenharmony_ci } else { 3388c2ecf20Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_SYNC_0_CS, CMDQ_SYNC_0_CS_SEV); 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_SYNC_0_MSH, ARM_SMMU_SH_ISH); 3418c2ecf20Sopenharmony_ci cmd[0] |= FIELD_PREP(CMDQ_SYNC_0_MSIATTR, ARM_SMMU_MEMATTR_OIWB); 3428c2ecf20Sopenharmony_ci break; 3438c2ecf20Sopenharmony_ci default: 3448c2ecf20Sopenharmony_ci return -ENOENT; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci return 0; 3488c2ecf20Sopenharmony_ci} 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_cistatic void arm_smmu_cmdq_build_sync_cmd(u64 *cmd, struct arm_smmu_device *smmu, 3518c2ecf20Sopenharmony_ci u32 prod) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci struct arm_smmu_queue *q = &smmu->cmdq.q; 3548c2ecf20Sopenharmony_ci struct arm_smmu_cmdq_ent ent = { 3558c2ecf20Sopenharmony_ci .opcode = CMDQ_OP_CMD_SYNC, 3568c2ecf20Sopenharmony_ci }; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci /* 3598c2ecf20Sopenharmony_ci * Beware that Hi16xx adds an extra 32 bits of goodness to its MSI 3608c2ecf20Sopenharmony_ci * payload, so the write will zero the entire command on that platform. 3618c2ecf20Sopenharmony_ci */ 3628c2ecf20Sopenharmony_ci if (smmu->options & ARM_SMMU_OPT_MSIPOLL) { 3638c2ecf20Sopenharmony_ci ent.sync.msiaddr = q->base_dma + Q_IDX(&q->llq, prod) * 3648c2ecf20Sopenharmony_ci q->ent_dwords * 8; 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci arm_smmu_cmdq_build_cmd(cmd, &ent); 3688c2ecf20Sopenharmony_ci} 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_cistatic void arm_smmu_cmdq_skip_err(struct arm_smmu_device *smmu) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci static const char *cerror_str[] = { 3738c2ecf20Sopenharmony_ci [CMDQ_ERR_CERROR_NONE_IDX] = "No error", 3748c2ecf20Sopenharmony_ci [CMDQ_ERR_CERROR_ILL_IDX] = "Illegal command", 3758c2ecf20Sopenharmony_ci [CMDQ_ERR_CERROR_ABT_IDX] = "Abort on command fetch", 3768c2ecf20Sopenharmony_ci [CMDQ_ERR_CERROR_ATC_INV_IDX] = "ATC invalidate timeout", 3778c2ecf20Sopenharmony_ci }; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci int i; 3808c2ecf20Sopenharmony_ci u64 cmd[CMDQ_ENT_DWORDS]; 3818c2ecf20Sopenharmony_ci struct arm_smmu_queue *q = &smmu->cmdq.q; 3828c2ecf20Sopenharmony_ci u32 cons = readl_relaxed(q->cons_reg); 3838c2ecf20Sopenharmony_ci u32 idx = FIELD_GET(CMDQ_CONS_ERR, cons); 3848c2ecf20Sopenharmony_ci struct arm_smmu_cmdq_ent cmd_sync = { 3858c2ecf20Sopenharmony_ci .opcode = CMDQ_OP_CMD_SYNC, 3868c2ecf20Sopenharmony_ci }; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci dev_err(smmu->dev, "CMDQ error (cons 0x%08x): %s\n", cons, 3898c2ecf20Sopenharmony_ci idx < ARRAY_SIZE(cerror_str) ? cerror_str[idx] : "Unknown"); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci switch (idx) { 3928c2ecf20Sopenharmony_ci case CMDQ_ERR_CERROR_ABT_IDX: 3938c2ecf20Sopenharmony_ci dev_err(smmu->dev, "retrying command fetch\n"); 3948c2ecf20Sopenharmony_ci case CMDQ_ERR_CERROR_NONE_IDX: 3958c2ecf20Sopenharmony_ci return; 3968c2ecf20Sopenharmony_ci case CMDQ_ERR_CERROR_ATC_INV_IDX: 3978c2ecf20Sopenharmony_ci /* 3988c2ecf20Sopenharmony_ci * ATC Invalidation Completion timeout. CONS is still pointing 3998c2ecf20Sopenharmony_ci * at the CMD_SYNC. Attempt to complete other pending commands 4008c2ecf20Sopenharmony_ci * by repeating the CMD_SYNC, though we might well end up back 4018c2ecf20Sopenharmony_ci * here since the ATC invalidation may still be pending. 4028c2ecf20Sopenharmony_ci */ 4038c2ecf20Sopenharmony_ci return; 4048c2ecf20Sopenharmony_ci case CMDQ_ERR_CERROR_ILL_IDX: 4058c2ecf20Sopenharmony_ci default: 4068c2ecf20Sopenharmony_ci break; 4078c2ecf20Sopenharmony_ci } 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci /* 4108c2ecf20Sopenharmony_ci * We may have concurrent producers, so we need to be careful 4118c2ecf20Sopenharmony_ci * not to touch any of the shadow cmdq state. 4128c2ecf20Sopenharmony_ci */ 4138c2ecf20Sopenharmony_ci queue_read(cmd, Q_ENT(q, cons), q->ent_dwords); 4148c2ecf20Sopenharmony_ci dev_err(smmu->dev, "skipping command in error state:\n"); 4158c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(cmd); ++i) 4168c2ecf20Sopenharmony_ci dev_err(smmu->dev, "\t0x%016llx\n", (unsigned long long)cmd[i]); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci /* Convert the erroneous command into a CMD_SYNC */ 4198c2ecf20Sopenharmony_ci if (arm_smmu_cmdq_build_cmd(cmd, &cmd_sync)) { 4208c2ecf20Sopenharmony_ci dev_err(smmu->dev, "failed to convert to CMD_SYNC\n"); 4218c2ecf20Sopenharmony_ci return; 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci queue_write(Q_ENT(q, cons), cmd, q->ent_dwords); 4258c2ecf20Sopenharmony_ci} 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci/* 4288c2ecf20Sopenharmony_ci * Command queue locking. 4298c2ecf20Sopenharmony_ci * This is a form of bastardised rwlock with the following major changes: 4308c2ecf20Sopenharmony_ci * 4318c2ecf20Sopenharmony_ci * - The only LOCK routines are exclusive_trylock() and shared_lock(). 4328c2ecf20Sopenharmony_ci * Neither have barrier semantics, and instead provide only a control 4338c2ecf20Sopenharmony_ci * dependency. 4348c2ecf20Sopenharmony_ci * 4358c2ecf20Sopenharmony_ci * - The UNLOCK routines are supplemented with shared_tryunlock(), which 4368c2ecf20Sopenharmony_ci * fails if the caller appears to be the last lock holder (yes, this is 4378c2ecf20Sopenharmony_ci * racy). All successful UNLOCK routines have RELEASE semantics. 4388c2ecf20Sopenharmony_ci */ 4398c2ecf20Sopenharmony_cistatic void arm_smmu_cmdq_shared_lock(struct arm_smmu_cmdq *cmdq) 4408c2ecf20Sopenharmony_ci{ 4418c2ecf20Sopenharmony_ci int val; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci /* 4448c2ecf20Sopenharmony_ci * We can try to avoid the cmpxchg() loop by simply incrementing the 4458c2ecf20Sopenharmony_ci * lock counter. When held in exclusive state, the lock counter is set 4468c2ecf20Sopenharmony_ci * to INT_MIN so these increments won't hurt as the value will remain 4478c2ecf20Sopenharmony_ci * negative. 4488c2ecf20Sopenharmony_ci */ 4498c2ecf20Sopenharmony_ci if (atomic_fetch_inc_relaxed(&cmdq->lock) >= 0) 4508c2ecf20Sopenharmony_ci return; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci do { 4538c2ecf20Sopenharmony_ci val = atomic_cond_read_relaxed(&cmdq->lock, VAL >= 0); 4548c2ecf20Sopenharmony_ci } while (atomic_cmpxchg_relaxed(&cmdq->lock, val, val + 1) != val); 4558c2ecf20Sopenharmony_ci} 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_cistatic void arm_smmu_cmdq_shared_unlock(struct arm_smmu_cmdq *cmdq) 4588c2ecf20Sopenharmony_ci{ 4598c2ecf20Sopenharmony_ci (void)atomic_dec_return_release(&cmdq->lock); 4608c2ecf20Sopenharmony_ci} 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_cistatic bool arm_smmu_cmdq_shared_tryunlock(struct arm_smmu_cmdq *cmdq) 4638c2ecf20Sopenharmony_ci{ 4648c2ecf20Sopenharmony_ci if (atomic_read(&cmdq->lock) == 1) 4658c2ecf20Sopenharmony_ci return false; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci arm_smmu_cmdq_shared_unlock(cmdq); 4688c2ecf20Sopenharmony_ci return true; 4698c2ecf20Sopenharmony_ci} 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci#define arm_smmu_cmdq_exclusive_trylock_irqsave(cmdq, flags) \ 4728c2ecf20Sopenharmony_ci({ \ 4738c2ecf20Sopenharmony_ci bool __ret; \ 4748c2ecf20Sopenharmony_ci local_irq_save(flags); \ 4758c2ecf20Sopenharmony_ci __ret = !atomic_cmpxchg_relaxed(&cmdq->lock, 0, INT_MIN); \ 4768c2ecf20Sopenharmony_ci if (!__ret) \ 4778c2ecf20Sopenharmony_ci local_irq_restore(flags); \ 4788c2ecf20Sopenharmony_ci __ret; \ 4798c2ecf20Sopenharmony_ci}) 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci#define arm_smmu_cmdq_exclusive_unlock_irqrestore(cmdq, flags) \ 4828c2ecf20Sopenharmony_ci({ \ 4838c2ecf20Sopenharmony_ci atomic_set_release(&cmdq->lock, 0); \ 4848c2ecf20Sopenharmony_ci local_irq_restore(flags); \ 4858c2ecf20Sopenharmony_ci}) 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci/* 4898c2ecf20Sopenharmony_ci * Command queue insertion. 4908c2ecf20Sopenharmony_ci * This is made fiddly by our attempts to achieve some sort of scalability 4918c2ecf20Sopenharmony_ci * since there is one queue shared amongst all of the CPUs in the system. If 4928c2ecf20Sopenharmony_ci * you like mixed-size concurrency, dependency ordering and relaxed atomics, 4938c2ecf20Sopenharmony_ci * then you'll *love* this monstrosity. 4948c2ecf20Sopenharmony_ci * 4958c2ecf20Sopenharmony_ci * The basic idea is to split the queue up into ranges of commands that are 4968c2ecf20Sopenharmony_ci * owned by a given CPU; the owner may not have written all of the commands 4978c2ecf20Sopenharmony_ci * itself, but is responsible for advancing the hardware prod pointer when 4988c2ecf20Sopenharmony_ci * the time comes. The algorithm is roughly: 4998c2ecf20Sopenharmony_ci * 5008c2ecf20Sopenharmony_ci * 1. Allocate some space in the queue. At this point we also discover 5018c2ecf20Sopenharmony_ci * whether the head of the queue is currently owned by another CPU, 5028c2ecf20Sopenharmony_ci * or whether we are the owner. 5038c2ecf20Sopenharmony_ci * 5048c2ecf20Sopenharmony_ci * 2. Write our commands into our allocated slots in the queue. 5058c2ecf20Sopenharmony_ci * 5068c2ecf20Sopenharmony_ci * 3. Mark our slots as valid in arm_smmu_cmdq.valid_map. 5078c2ecf20Sopenharmony_ci * 5088c2ecf20Sopenharmony_ci * 4. If we are an owner: 5098c2ecf20Sopenharmony_ci * a. Wait for the previous owner to finish. 5108c2ecf20Sopenharmony_ci * b. Mark the queue head as unowned, which tells us the range 5118c2ecf20Sopenharmony_ci * that we are responsible for publishing. 5128c2ecf20Sopenharmony_ci * c. Wait for all commands in our owned range to become valid. 5138c2ecf20Sopenharmony_ci * d. Advance the hardware prod pointer. 5148c2ecf20Sopenharmony_ci * e. Tell the next owner we've finished. 5158c2ecf20Sopenharmony_ci * 5168c2ecf20Sopenharmony_ci * 5. If we are inserting a CMD_SYNC (we may or may not have been an 5178c2ecf20Sopenharmony_ci * owner), then we need to stick around until it has completed: 5188c2ecf20Sopenharmony_ci * a. If we have MSIs, the SMMU can write back into the CMD_SYNC 5198c2ecf20Sopenharmony_ci * to clear the first 4 bytes. 5208c2ecf20Sopenharmony_ci * b. Otherwise, we spin waiting for the hardware cons pointer to 5218c2ecf20Sopenharmony_ci * advance past our command. 5228c2ecf20Sopenharmony_ci * 5238c2ecf20Sopenharmony_ci * The devil is in the details, particularly the use of locking for handling 5248c2ecf20Sopenharmony_ci * SYNC completion and freeing up space in the queue before we think that it is 5258c2ecf20Sopenharmony_ci * full. 5268c2ecf20Sopenharmony_ci */ 5278c2ecf20Sopenharmony_cistatic void __arm_smmu_cmdq_poll_set_valid_map(struct arm_smmu_cmdq *cmdq, 5288c2ecf20Sopenharmony_ci u32 sprod, u32 eprod, bool set) 5298c2ecf20Sopenharmony_ci{ 5308c2ecf20Sopenharmony_ci u32 swidx, sbidx, ewidx, ebidx; 5318c2ecf20Sopenharmony_ci struct arm_smmu_ll_queue llq = { 5328c2ecf20Sopenharmony_ci .max_n_shift = cmdq->q.llq.max_n_shift, 5338c2ecf20Sopenharmony_ci .prod = sprod, 5348c2ecf20Sopenharmony_ci }; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci ewidx = BIT_WORD(Q_IDX(&llq, eprod)); 5378c2ecf20Sopenharmony_ci ebidx = Q_IDX(&llq, eprod) % BITS_PER_LONG; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci while (llq.prod != eprod) { 5408c2ecf20Sopenharmony_ci unsigned long mask; 5418c2ecf20Sopenharmony_ci atomic_long_t *ptr; 5428c2ecf20Sopenharmony_ci u32 limit = BITS_PER_LONG; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci swidx = BIT_WORD(Q_IDX(&llq, llq.prod)); 5458c2ecf20Sopenharmony_ci sbidx = Q_IDX(&llq, llq.prod) % BITS_PER_LONG; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci ptr = &cmdq->valid_map[swidx]; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci if ((swidx == ewidx) && (sbidx < ebidx)) 5508c2ecf20Sopenharmony_ci limit = ebidx; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci mask = GENMASK(limit - 1, sbidx); 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci /* 5558c2ecf20Sopenharmony_ci * The valid bit is the inverse of the wrap bit. This means 5568c2ecf20Sopenharmony_ci * that a zero-initialised queue is invalid and, after marking 5578c2ecf20Sopenharmony_ci * all entries as valid, they become invalid again when we 5588c2ecf20Sopenharmony_ci * wrap. 5598c2ecf20Sopenharmony_ci */ 5608c2ecf20Sopenharmony_ci if (set) { 5618c2ecf20Sopenharmony_ci atomic_long_xor(mask, ptr); 5628c2ecf20Sopenharmony_ci } else { /* Poll */ 5638c2ecf20Sopenharmony_ci unsigned long valid; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci valid = (ULONG_MAX + !!Q_WRP(&llq, llq.prod)) & mask; 5668c2ecf20Sopenharmony_ci atomic_long_cond_read_relaxed(ptr, (VAL & mask) == valid); 5678c2ecf20Sopenharmony_ci } 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci llq.prod = queue_inc_prod_n(&llq, limit - sbidx); 5708c2ecf20Sopenharmony_ci } 5718c2ecf20Sopenharmony_ci} 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci/* Mark all entries in the range [sprod, eprod) as valid */ 5748c2ecf20Sopenharmony_cistatic void arm_smmu_cmdq_set_valid_map(struct arm_smmu_cmdq *cmdq, 5758c2ecf20Sopenharmony_ci u32 sprod, u32 eprod) 5768c2ecf20Sopenharmony_ci{ 5778c2ecf20Sopenharmony_ci __arm_smmu_cmdq_poll_set_valid_map(cmdq, sprod, eprod, true); 5788c2ecf20Sopenharmony_ci} 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci/* Wait for all entries in the range [sprod, eprod) to become valid */ 5818c2ecf20Sopenharmony_cistatic void arm_smmu_cmdq_poll_valid_map(struct arm_smmu_cmdq *cmdq, 5828c2ecf20Sopenharmony_ci u32 sprod, u32 eprod) 5838c2ecf20Sopenharmony_ci{ 5848c2ecf20Sopenharmony_ci __arm_smmu_cmdq_poll_set_valid_map(cmdq, sprod, eprod, false); 5858c2ecf20Sopenharmony_ci} 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci/* Wait for the command queue to become non-full */ 5888c2ecf20Sopenharmony_cistatic int arm_smmu_cmdq_poll_until_not_full(struct arm_smmu_device *smmu, 5898c2ecf20Sopenharmony_ci struct arm_smmu_ll_queue *llq) 5908c2ecf20Sopenharmony_ci{ 5918c2ecf20Sopenharmony_ci unsigned long flags; 5928c2ecf20Sopenharmony_ci struct arm_smmu_queue_poll qp; 5938c2ecf20Sopenharmony_ci struct arm_smmu_cmdq *cmdq = &smmu->cmdq; 5948c2ecf20Sopenharmony_ci int ret = 0; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci /* 5978c2ecf20Sopenharmony_ci * Try to update our copy of cons by grabbing exclusive cmdq access. If 5988c2ecf20Sopenharmony_ci * that fails, spin until somebody else updates it for us. 5998c2ecf20Sopenharmony_ci */ 6008c2ecf20Sopenharmony_ci if (arm_smmu_cmdq_exclusive_trylock_irqsave(cmdq, flags)) { 6018c2ecf20Sopenharmony_ci WRITE_ONCE(cmdq->q.llq.cons, readl_relaxed(cmdq->q.cons_reg)); 6028c2ecf20Sopenharmony_ci arm_smmu_cmdq_exclusive_unlock_irqrestore(cmdq, flags); 6038c2ecf20Sopenharmony_ci llq->val = READ_ONCE(cmdq->q.llq.val); 6048c2ecf20Sopenharmony_ci return 0; 6058c2ecf20Sopenharmony_ci } 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci queue_poll_init(smmu, &qp); 6088c2ecf20Sopenharmony_ci do { 6098c2ecf20Sopenharmony_ci llq->val = READ_ONCE(smmu->cmdq.q.llq.val); 6108c2ecf20Sopenharmony_ci if (!queue_full(llq)) 6118c2ecf20Sopenharmony_ci break; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci ret = queue_poll(&qp); 6148c2ecf20Sopenharmony_ci } while (!ret); 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci return ret; 6178c2ecf20Sopenharmony_ci} 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci/* 6208c2ecf20Sopenharmony_ci * Wait until the SMMU signals a CMD_SYNC completion MSI. 6218c2ecf20Sopenharmony_ci * Must be called with the cmdq lock held in some capacity. 6228c2ecf20Sopenharmony_ci */ 6238c2ecf20Sopenharmony_cistatic int __arm_smmu_cmdq_poll_until_msi(struct arm_smmu_device *smmu, 6248c2ecf20Sopenharmony_ci struct arm_smmu_ll_queue *llq) 6258c2ecf20Sopenharmony_ci{ 6268c2ecf20Sopenharmony_ci int ret = 0; 6278c2ecf20Sopenharmony_ci struct arm_smmu_queue_poll qp; 6288c2ecf20Sopenharmony_ci struct arm_smmu_cmdq *cmdq = &smmu->cmdq; 6298c2ecf20Sopenharmony_ci u32 *cmd = (u32 *)(Q_ENT(&cmdq->q, llq->prod)); 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci queue_poll_init(smmu, &qp); 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci /* 6348c2ecf20Sopenharmony_ci * The MSI won't generate an event, since it's being written back 6358c2ecf20Sopenharmony_ci * into the command queue. 6368c2ecf20Sopenharmony_ci */ 6378c2ecf20Sopenharmony_ci qp.wfe = false; 6388c2ecf20Sopenharmony_ci smp_cond_load_relaxed(cmd, !VAL || (ret = queue_poll(&qp))); 6398c2ecf20Sopenharmony_ci llq->cons = ret ? llq->prod : queue_inc_prod_n(llq, 1); 6408c2ecf20Sopenharmony_ci return ret; 6418c2ecf20Sopenharmony_ci} 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci/* 6448c2ecf20Sopenharmony_ci * Wait until the SMMU cons index passes llq->prod. 6458c2ecf20Sopenharmony_ci * Must be called with the cmdq lock held in some capacity. 6468c2ecf20Sopenharmony_ci */ 6478c2ecf20Sopenharmony_cistatic int __arm_smmu_cmdq_poll_until_consumed(struct arm_smmu_device *smmu, 6488c2ecf20Sopenharmony_ci struct arm_smmu_ll_queue *llq) 6498c2ecf20Sopenharmony_ci{ 6508c2ecf20Sopenharmony_ci struct arm_smmu_queue_poll qp; 6518c2ecf20Sopenharmony_ci struct arm_smmu_cmdq *cmdq = &smmu->cmdq; 6528c2ecf20Sopenharmony_ci u32 prod = llq->prod; 6538c2ecf20Sopenharmony_ci int ret = 0; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci queue_poll_init(smmu, &qp); 6568c2ecf20Sopenharmony_ci llq->val = READ_ONCE(smmu->cmdq.q.llq.val); 6578c2ecf20Sopenharmony_ci do { 6588c2ecf20Sopenharmony_ci if (queue_consumed(llq, prod)) 6598c2ecf20Sopenharmony_ci break; 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci ret = queue_poll(&qp); 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci /* 6648c2ecf20Sopenharmony_ci * This needs to be a readl() so that our subsequent call 6658c2ecf20Sopenharmony_ci * to arm_smmu_cmdq_shared_tryunlock() can fail accurately. 6668c2ecf20Sopenharmony_ci * 6678c2ecf20Sopenharmony_ci * Specifically, we need to ensure that we observe all 6688c2ecf20Sopenharmony_ci * shared_lock()s by other CMD_SYNCs that share our owner, 6698c2ecf20Sopenharmony_ci * so that a failing call to tryunlock() means that we're 6708c2ecf20Sopenharmony_ci * the last one out and therefore we can safely advance 6718c2ecf20Sopenharmony_ci * cmdq->q.llq.cons. Roughly speaking: 6728c2ecf20Sopenharmony_ci * 6738c2ecf20Sopenharmony_ci * CPU 0 CPU1 CPU2 (us) 6748c2ecf20Sopenharmony_ci * 6758c2ecf20Sopenharmony_ci * if (sync) 6768c2ecf20Sopenharmony_ci * shared_lock(); 6778c2ecf20Sopenharmony_ci * 6788c2ecf20Sopenharmony_ci * dma_wmb(); 6798c2ecf20Sopenharmony_ci * set_valid_map(); 6808c2ecf20Sopenharmony_ci * 6818c2ecf20Sopenharmony_ci * if (owner) { 6828c2ecf20Sopenharmony_ci * poll_valid_map(); 6838c2ecf20Sopenharmony_ci * <control dependency> 6848c2ecf20Sopenharmony_ci * writel(prod_reg); 6858c2ecf20Sopenharmony_ci * 6868c2ecf20Sopenharmony_ci * readl(cons_reg); 6878c2ecf20Sopenharmony_ci * tryunlock(); 6888c2ecf20Sopenharmony_ci * 6898c2ecf20Sopenharmony_ci * Requires us to see CPU 0's shared_lock() acquisition. 6908c2ecf20Sopenharmony_ci */ 6918c2ecf20Sopenharmony_ci llq->cons = readl(cmdq->q.cons_reg); 6928c2ecf20Sopenharmony_ci } while (!ret); 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci return ret; 6958c2ecf20Sopenharmony_ci} 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_cistatic int arm_smmu_cmdq_poll_until_sync(struct arm_smmu_device *smmu, 6988c2ecf20Sopenharmony_ci struct arm_smmu_ll_queue *llq) 6998c2ecf20Sopenharmony_ci{ 7008c2ecf20Sopenharmony_ci if (smmu->options & ARM_SMMU_OPT_MSIPOLL) 7018c2ecf20Sopenharmony_ci return __arm_smmu_cmdq_poll_until_msi(smmu, llq); 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci return __arm_smmu_cmdq_poll_until_consumed(smmu, llq); 7048c2ecf20Sopenharmony_ci} 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_cistatic void arm_smmu_cmdq_write_entries(struct arm_smmu_cmdq *cmdq, u64 *cmds, 7078c2ecf20Sopenharmony_ci u32 prod, int n) 7088c2ecf20Sopenharmony_ci{ 7098c2ecf20Sopenharmony_ci int i; 7108c2ecf20Sopenharmony_ci struct arm_smmu_ll_queue llq = { 7118c2ecf20Sopenharmony_ci .max_n_shift = cmdq->q.llq.max_n_shift, 7128c2ecf20Sopenharmony_ci .prod = prod, 7138c2ecf20Sopenharmony_ci }; 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci for (i = 0; i < n; ++i) { 7168c2ecf20Sopenharmony_ci u64 *cmd = &cmds[i * CMDQ_ENT_DWORDS]; 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci prod = queue_inc_prod_n(&llq, i); 7198c2ecf20Sopenharmony_ci queue_write(Q_ENT(&cmdq->q, prod), cmd, CMDQ_ENT_DWORDS); 7208c2ecf20Sopenharmony_ci } 7218c2ecf20Sopenharmony_ci} 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci/* 7248c2ecf20Sopenharmony_ci * This is the actual insertion function, and provides the following 7258c2ecf20Sopenharmony_ci * ordering guarantees to callers: 7268c2ecf20Sopenharmony_ci * 7278c2ecf20Sopenharmony_ci * - There is a dma_wmb() before publishing any commands to the queue. 7288c2ecf20Sopenharmony_ci * This can be relied upon to order prior writes to data structures 7298c2ecf20Sopenharmony_ci * in memory (such as a CD or an STE) before the command. 7308c2ecf20Sopenharmony_ci * 7318c2ecf20Sopenharmony_ci * - On completion of a CMD_SYNC, there is a control dependency. 7328c2ecf20Sopenharmony_ci * This can be relied upon to order subsequent writes to memory (e.g. 7338c2ecf20Sopenharmony_ci * freeing an IOVA) after completion of the CMD_SYNC. 7348c2ecf20Sopenharmony_ci * 7358c2ecf20Sopenharmony_ci * - Command insertion is totally ordered, so if two CPUs each race to 7368c2ecf20Sopenharmony_ci * insert their own list of commands then all of the commands from one 7378c2ecf20Sopenharmony_ci * CPU will appear before any of the commands from the other CPU. 7388c2ecf20Sopenharmony_ci */ 7398c2ecf20Sopenharmony_cistatic int arm_smmu_cmdq_issue_cmdlist(struct arm_smmu_device *smmu, 7408c2ecf20Sopenharmony_ci u64 *cmds, int n, bool sync) 7418c2ecf20Sopenharmony_ci{ 7428c2ecf20Sopenharmony_ci u64 cmd_sync[CMDQ_ENT_DWORDS]; 7438c2ecf20Sopenharmony_ci u32 prod; 7448c2ecf20Sopenharmony_ci unsigned long flags; 7458c2ecf20Sopenharmony_ci bool owner; 7468c2ecf20Sopenharmony_ci struct arm_smmu_cmdq *cmdq = &smmu->cmdq; 7478c2ecf20Sopenharmony_ci struct arm_smmu_ll_queue llq = { 7488c2ecf20Sopenharmony_ci .max_n_shift = cmdq->q.llq.max_n_shift, 7498c2ecf20Sopenharmony_ci }, head = llq; 7508c2ecf20Sopenharmony_ci int ret = 0; 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci /* 1. Allocate some space in the queue */ 7538c2ecf20Sopenharmony_ci local_irq_save(flags); 7548c2ecf20Sopenharmony_ci llq.val = READ_ONCE(cmdq->q.llq.val); 7558c2ecf20Sopenharmony_ci do { 7568c2ecf20Sopenharmony_ci u64 old; 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci while (!queue_has_space(&llq, n + sync)) { 7598c2ecf20Sopenharmony_ci local_irq_restore(flags); 7608c2ecf20Sopenharmony_ci if (arm_smmu_cmdq_poll_until_not_full(smmu, &llq)) 7618c2ecf20Sopenharmony_ci dev_err_ratelimited(smmu->dev, "CMDQ timeout\n"); 7628c2ecf20Sopenharmony_ci local_irq_save(flags); 7638c2ecf20Sopenharmony_ci } 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci head.cons = llq.cons; 7668c2ecf20Sopenharmony_ci head.prod = queue_inc_prod_n(&llq, n + sync) | 7678c2ecf20Sopenharmony_ci CMDQ_PROD_OWNED_FLAG; 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci old = cmpxchg_relaxed(&cmdq->q.llq.val, llq.val, head.val); 7708c2ecf20Sopenharmony_ci if (old == llq.val) 7718c2ecf20Sopenharmony_ci break; 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci llq.val = old; 7748c2ecf20Sopenharmony_ci } while (1); 7758c2ecf20Sopenharmony_ci owner = !(llq.prod & CMDQ_PROD_OWNED_FLAG); 7768c2ecf20Sopenharmony_ci head.prod &= ~CMDQ_PROD_OWNED_FLAG; 7778c2ecf20Sopenharmony_ci llq.prod &= ~CMDQ_PROD_OWNED_FLAG; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci /* 7808c2ecf20Sopenharmony_ci * 2. Write our commands into the queue 7818c2ecf20Sopenharmony_ci * Dependency ordering from the cmpxchg() loop above. 7828c2ecf20Sopenharmony_ci */ 7838c2ecf20Sopenharmony_ci arm_smmu_cmdq_write_entries(cmdq, cmds, llq.prod, n); 7848c2ecf20Sopenharmony_ci if (sync) { 7858c2ecf20Sopenharmony_ci prod = queue_inc_prod_n(&llq, n); 7868c2ecf20Sopenharmony_ci arm_smmu_cmdq_build_sync_cmd(cmd_sync, smmu, prod); 7878c2ecf20Sopenharmony_ci queue_write(Q_ENT(&cmdq->q, prod), cmd_sync, CMDQ_ENT_DWORDS); 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci /* 7908c2ecf20Sopenharmony_ci * In order to determine completion of our CMD_SYNC, we must 7918c2ecf20Sopenharmony_ci * ensure that the queue can't wrap twice without us noticing. 7928c2ecf20Sopenharmony_ci * We achieve that by taking the cmdq lock as shared before 7938c2ecf20Sopenharmony_ci * marking our slot as valid. 7948c2ecf20Sopenharmony_ci */ 7958c2ecf20Sopenharmony_ci arm_smmu_cmdq_shared_lock(cmdq); 7968c2ecf20Sopenharmony_ci } 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci /* 3. Mark our slots as valid, ensuring commands are visible first */ 7998c2ecf20Sopenharmony_ci dma_wmb(); 8008c2ecf20Sopenharmony_ci arm_smmu_cmdq_set_valid_map(cmdq, llq.prod, head.prod); 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci /* 4. If we are the owner, take control of the SMMU hardware */ 8038c2ecf20Sopenharmony_ci if (owner) { 8048c2ecf20Sopenharmony_ci /* a. Wait for previous owner to finish */ 8058c2ecf20Sopenharmony_ci atomic_cond_read_relaxed(&cmdq->owner_prod, VAL == llq.prod); 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci /* b. Stop gathering work by clearing the owned flag */ 8088c2ecf20Sopenharmony_ci prod = atomic_fetch_andnot_relaxed(CMDQ_PROD_OWNED_FLAG, 8098c2ecf20Sopenharmony_ci &cmdq->q.llq.atomic.prod); 8108c2ecf20Sopenharmony_ci prod &= ~CMDQ_PROD_OWNED_FLAG; 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci /* 8138c2ecf20Sopenharmony_ci * c. Wait for any gathered work to be written to the queue. 8148c2ecf20Sopenharmony_ci * Note that we read our own entries so that we have the control 8158c2ecf20Sopenharmony_ci * dependency required by (d). 8168c2ecf20Sopenharmony_ci */ 8178c2ecf20Sopenharmony_ci arm_smmu_cmdq_poll_valid_map(cmdq, llq.prod, prod); 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci /* 8208c2ecf20Sopenharmony_ci * d. Advance the hardware prod pointer 8218c2ecf20Sopenharmony_ci * Control dependency ordering from the entries becoming valid. 8228c2ecf20Sopenharmony_ci */ 8238c2ecf20Sopenharmony_ci writel_relaxed(prod, cmdq->q.prod_reg); 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci /* 8268c2ecf20Sopenharmony_ci * e. Tell the next owner we're done 8278c2ecf20Sopenharmony_ci * Make sure we've updated the hardware first, so that we don't 8288c2ecf20Sopenharmony_ci * race to update prod and potentially move it backwards. 8298c2ecf20Sopenharmony_ci */ 8308c2ecf20Sopenharmony_ci atomic_set_release(&cmdq->owner_prod, prod); 8318c2ecf20Sopenharmony_ci } 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci /* 5. If we are inserting a CMD_SYNC, we must wait for it to complete */ 8348c2ecf20Sopenharmony_ci if (sync) { 8358c2ecf20Sopenharmony_ci llq.prod = queue_inc_prod_n(&llq, n); 8368c2ecf20Sopenharmony_ci ret = arm_smmu_cmdq_poll_until_sync(smmu, &llq); 8378c2ecf20Sopenharmony_ci if (ret) { 8388c2ecf20Sopenharmony_ci dev_err_ratelimited(smmu->dev, 8398c2ecf20Sopenharmony_ci "CMD_SYNC timeout at 0x%08x [hwprod 0x%08x, hwcons 0x%08x]\n", 8408c2ecf20Sopenharmony_ci llq.prod, 8418c2ecf20Sopenharmony_ci readl_relaxed(cmdq->q.prod_reg), 8428c2ecf20Sopenharmony_ci readl_relaxed(cmdq->q.cons_reg)); 8438c2ecf20Sopenharmony_ci } 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci /* 8468c2ecf20Sopenharmony_ci * Try to unlock the cmdq lock. This will fail if we're the last 8478c2ecf20Sopenharmony_ci * reader, in which case we can safely update cmdq->q.llq.cons 8488c2ecf20Sopenharmony_ci */ 8498c2ecf20Sopenharmony_ci if (!arm_smmu_cmdq_shared_tryunlock(cmdq)) { 8508c2ecf20Sopenharmony_ci WRITE_ONCE(cmdq->q.llq.cons, llq.cons); 8518c2ecf20Sopenharmony_ci arm_smmu_cmdq_shared_unlock(cmdq); 8528c2ecf20Sopenharmony_ci } 8538c2ecf20Sopenharmony_ci } 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci local_irq_restore(flags); 8568c2ecf20Sopenharmony_ci return ret; 8578c2ecf20Sopenharmony_ci} 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_cistatic int arm_smmu_cmdq_issue_cmd(struct arm_smmu_device *smmu, 8608c2ecf20Sopenharmony_ci struct arm_smmu_cmdq_ent *ent) 8618c2ecf20Sopenharmony_ci{ 8628c2ecf20Sopenharmony_ci u64 cmd[CMDQ_ENT_DWORDS]; 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci if (arm_smmu_cmdq_build_cmd(cmd, ent)) { 8658c2ecf20Sopenharmony_ci dev_warn(smmu->dev, "ignoring unknown CMDQ opcode 0x%x\n", 8668c2ecf20Sopenharmony_ci ent->opcode); 8678c2ecf20Sopenharmony_ci return -EINVAL; 8688c2ecf20Sopenharmony_ci } 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci return arm_smmu_cmdq_issue_cmdlist(smmu, cmd, 1, false); 8718c2ecf20Sopenharmony_ci} 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_cistatic int arm_smmu_cmdq_issue_sync(struct arm_smmu_device *smmu) 8748c2ecf20Sopenharmony_ci{ 8758c2ecf20Sopenharmony_ci return arm_smmu_cmdq_issue_cmdlist(smmu, NULL, 0, true); 8768c2ecf20Sopenharmony_ci} 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_cistatic void arm_smmu_cmdq_batch_add(struct arm_smmu_device *smmu, 8798c2ecf20Sopenharmony_ci struct arm_smmu_cmdq_batch *cmds, 8808c2ecf20Sopenharmony_ci struct arm_smmu_cmdq_ent *cmd) 8818c2ecf20Sopenharmony_ci{ 8828c2ecf20Sopenharmony_ci if (cmds->num == CMDQ_BATCH_ENTRIES) { 8838c2ecf20Sopenharmony_ci arm_smmu_cmdq_issue_cmdlist(smmu, cmds->cmds, cmds->num, false); 8848c2ecf20Sopenharmony_ci cmds->num = 0; 8858c2ecf20Sopenharmony_ci } 8868c2ecf20Sopenharmony_ci arm_smmu_cmdq_build_cmd(&cmds->cmds[cmds->num * CMDQ_ENT_DWORDS], cmd); 8878c2ecf20Sopenharmony_ci cmds->num++; 8888c2ecf20Sopenharmony_ci} 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_cistatic int arm_smmu_cmdq_batch_submit(struct arm_smmu_device *smmu, 8918c2ecf20Sopenharmony_ci struct arm_smmu_cmdq_batch *cmds) 8928c2ecf20Sopenharmony_ci{ 8938c2ecf20Sopenharmony_ci return arm_smmu_cmdq_issue_cmdlist(smmu, cmds->cmds, cmds->num, true); 8948c2ecf20Sopenharmony_ci} 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci/* Context descriptor manipulation functions */ 8978c2ecf20Sopenharmony_civoid arm_smmu_tlb_inv_asid(struct arm_smmu_device *smmu, u16 asid) 8988c2ecf20Sopenharmony_ci{ 8998c2ecf20Sopenharmony_ci struct arm_smmu_cmdq_ent cmd = { 9008c2ecf20Sopenharmony_ci .opcode = CMDQ_OP_TLBI_NH_ASID, 9018c2ecf20Sopenharmony_ci .tlbi.asid = asid, 9028c2ecf20Sopenharmony_ci }; 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci arm_smmu_cmdq_issue_cmd(smmu, &cmd); 9058c2ecf20Sopenharmony_ci arm_smmu_cmdq_issue_sync(smmu); 9068c2ecf20Sopenharmony_ci} 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_cistatic void arm_smmu_sync_cd(struct arm_smmu_domain *smmu_domain, 9098c2ecf20Sopenharmony_ci int ssid, bool leaf) 9108c2ecf20Sopenharmony_ci{ 9118c2ecf20Sopenharmony_ci size_t i; 9128c2ecf20Sopenharmony_ci unsigned long flags; 9138c2ecf20Sopenharmony_ci struct arm_smmu_master *master; 9148c2ecf20Sopenharmony_ci struct arm_smmu_cmdq_batch cmds = {}; 9158c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu = smmu_domain->smmu; 9168c2ecf20Sopenharmony_ci struct arm_smmu_cmdq_ent cmd = { 9178c2ecf20Sopenharmony_ci .opcode = CMDQ_OP_CFGI_CD, 9188c2ecf20Sopenharmony_ci .cfgi = { 9198c2ecf20Sopenharmony_ci .ssid = ssid, 9208c2ecf20Sopenharmony_ci .leaf = leaf, 9218c2ecf20Sopenharmony_ci }, 9228c2ecf20Sopenharmony_ci }; 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci spin_lock_irqsave(&smmu_domain->devices_lock, flags); 9258c2ecf20Sopenharmony_ci list_for_each_entry(master, &smmu_domain->devices, domain_head) { 9268c2ecf20Sopenharmony_ci for (i = 0; i < master->num_sids; i++) { 9278c2ecf20Sopenharmony_ci cmd.cfgi.sid = master->sids[i]; 9288c2ecf20Sopenharmony_ci arm_smmu_cmdq_batch_add(smmu, &cmds, &cmd); 9298c2ecf20Sopenharmony_ci } 9308c2ecf20Sopenharmony_ci } 9318c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&smmu_domain->devices_lock, flags); 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci arm_smmu_cmdq_batch_submit(smmu, &cmds); 9348c2ecf20Sopenharmony_ci} 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_cistatic int arm_smmu_alloc_cd_leaf_table(struct arm_smmu_device *smmu, 9378c2ecf20Sopenharmony_ci struct arm_smmu_l1_ctx_desc *l1_desc) 9388c2ecf20Sopenharmony_ci{ 9398c2ecf20Sopenharmony_ci size_t size = CTXDESC_L2_ENTRIES * (CTXDESC_CD_DWORDS << 3); 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci l1_desc->l2ptr = dmam_alloc_coherent(smmu->dev, size, 9428c2ecf20Sopenharmony_ci &l1_desc->l2ptr_dma, GFP_KERNEL); 9438c2ecf20Sopenharmony_ci if (!l1_desc->l2ptr) { 9448c2ecf20Sopenharmony_ci dev_warn(smmu->dev, 9458c2ecf20Sopenharmony_ci "failed to allocate context descriptor table\n"); 9468c2ecf20Sopenharmony_ci return -ENOMEM; 9478c2ecf20Sopenharmony_ci } 9488c2ecf20Sopenharmony_ci return 0; 9498c2ecf20Sopenharmony_ci} 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_cistatic void arm_smmu_write_cd_l1_desc(__le64 *dst, 9528c2ecf20Sopenharmony_ci struct arm_smmu_l1_ctx_desc *l1_desc) 9538c2ecf20Sopenharmony_ci{ 9548c2ecf20Sopenharmony_ci u64 val = (l1_desc->l2ptr_dma & CTXDESC_L1_DESC_L2PTR_MASK) | 9558c2ecf20Sopenharmony_ci CTXDESC_L1_DESC_V; 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci /* See comment in arm_smmu_write_ctx_desc() */ 9588c2ecf20Sopenharmony_ci WRITE_ONCE(*dst, cpu_to_le64(val)); 9598c2ecf20Sopenharmony_ci} 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_cistatic __le64 *arm_smmu_get_cd_ptr(struct arm_smmu_domain *smmu_domain, 9628c2ecf20Sopenharmony_ci u32 ssid) 9638c2ecf20Sopenharmony_ci{ 9648c2ecf20Sopenharmony_ci __le64 *l1ptr; 9658c2ecf20Sopenharmony_ci unsigned int idx; 9668c2ecf20Sopenharmony_ci struct arm_smmu_l1_ctx_desc *l1_desc; 9678c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu = smmu_domain->smmu; 9688c2ecf20Sopenharmony_ci struct arm_smmu_ctx_desc_cfg *cdcfg = &smmu_domain->s1_cfg.cdcfg; 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci if (smmu_domain->s1_cfg.s1fmt == STRTAB_STE_0_S1FMT_LINEAR) 9718c2ecf20Sopenharmony_ci return cdcfg->cdtab + ssid * CTXDESC_CD_DWORDS; 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci idx = ssid >> CTXDESC_SPLIT; 9748c2ecf20Sopenharmony_ci l1_desc = &cdcfg->l1_desc[idx]; 9758c2ecf20Sopenharmony_ci if (!l1_desc->l2ptr) { 9768c2ecf20Sopenharmony_ci if (arm_smmu_alloc_cd_leaf_table(smmu, l1_desc)) 9778c2ecf20Sopenharmony_ci return NULL; 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci l1ptr = cdcfg->cdtab + idx * CTXDESC_L1_DESC_DWORDS; 9808c2ecf20Sopenharmony_ci arm_smmu_write_cd_l1_desc(l1ptr, l1_desc); 9818c2ecf20Sopenharmony_ci /* An invalid L1CD can be cached */ 9828c2ecf20Sopenharmony_ci arm_smmu_sync_cd(smmu_domain, ssid, false); 9838c2ecf20Sopenharmony_ci } 9848c2ecf20Sopenharmony_ci idx = ssid & (CTXDESC_L2_ENTRIES - 1); 9858c2ecf20Sopenharmony_ci return l1_desc->l2ptr + idx * CTXDESC_CD_DWORDS; 9868c2ecf20Sopenharmony_ci} 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ciint arm_smmu_write_ctx_desc(struct arm_smmu_domain *smmu_domain, int ssid, 9898c2ecf20Sopenharmony_ci struct arm_smmu_ctx_desc *cd) 9908c2ecf20Sopenharmony_ci{ 9918c2ecf20Sopenharmony_ci /* 9928c2ecf20Sopenharmony_ci * This function handles the following cases: 9938c2ecf20Sopenharmony_ci * 9948c2ecf20Sopenharmony_ci * (1) Install primary CD, for normal DMA traffic (SSID = 0). 9958c2ecf20Sopenharmony_ci * (2) Install a secondary CD, for SID+SSID traffic. 9968c2ecf20Sopenharmony_ci * (3) Update ASID of a CD. Atomically write the first 64 bits of the 9978c2ecf20Sopenharmony_ci * CD, then invalidate the old entry and mappings. 9988c2ecf20Sopenharmony_ci * (4) Remove a secondary CD. 9998c2ecf20Sopenharmony_ci */ 10008c2ecf20Sopenharmony_ci u64 val; 10018c2ecf20Sopenharmony_ci bool cd_live; 10028c2ecf20Sopenharmony_ci __le64 *cdptr; 10038c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu = smmu_domain->smmu; 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci if (WARN_ON(ssid >= (1 << smmu_domain->s1_cfg.s1cdmax))) 10068c2ecf20Sopenharmony_ci return -E2BIG; 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci cdptr = arm_smmu_get_cd_ptr(smmu_domain, ssid); 10098c2ecf20Sopenharmony_ci if (!cdptr) 10108c2ecf20Sopenharmony_ci return -ENOMEM; 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci val = le64_to_cpu(cdptr[0]); 10138c2ecf20Sopenharmony_ci cd_live = !!(val & CTXDESC_CD_0_V); 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci if (!cd) { /* (4) */ 10168c2ecf20Sopenharmony_ci val = 0; 10178c2ecf20Sopenharmony_ci } else if (cd_live) { /* (3) */ 10188c2ecf20Sopenharmony_ci val &= ~CTXDESC_CD_0_ASID; 10198c2ecf20Sopenharmony_ci val |= FIELD_PREP(CTXDESC_CD_0_ASID, cd->asid); 10208c2ecf20Sopenharmony_ci /* 10218c2ecf20Sopenharmony_ci * Until CD+TLB invalidation, both ASIDs may be used for tagging 10228c2ecf20Sopenharmony_ci * this substream's traffic 10238c2ecf20Sopenharmony_ci */ 10248c2ecf20Sopenharmony_ci } else { /* (1) and (2) */ 10258c2ecf20Sopenharmony_ci cdptr[1] = cpu_to_le64(cd->ttbr & CTXDESC_CD_1_TTB0_MASK); 10268c2ecf20Sopenharmony_ci cdptr[2] = 0; 10278c2ecf20Sopenharmony_ci cdptr[3] = cpu_to_le64(cd->mair); 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci /* 10308c2ecf20Sopenharmony_ci * STE is live, and the SMMU might read dwords of this CD in any 10318c2ecf20Sopenharmony_ci * order. Ensure that it observes valid values before reading 10328c2ecf20Sopenharmony_ci * V=1. 10338c2ecf20Sopenharmony_ci */ 10348c2ecf20Sopenharmony_ci arm_smmu_sync_cd(smmu_domain, ssid, true); 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci val = cd->tcr | 10378c2ecf20Sopenharmony_ci#ifdef __BIG_ENDIAN 10388c2ecf20Sopenharmony_ci CTXDESC_CD_0_ENDI | 10398c2ecf20Sopenharmony_ci#endif 10408c2ecf20Sopenharmony_ci CTXDESC_CD_0_R | CTXDESC_CD_0_A | 10418c2ecf20Sopenharmony_ci (cd->mm ? 0 : CTXDESC_CD_0_ASET) | 10428c2ecf20Sopenharmony_ci CTXDESC_CD_0_AA64 | 10438c2ecf20Sopenharmony_ci FIELD_PREP(CTXDESC_CD_0_ASID, cd->asid) | 10448c2ecf20Sopenharmony_ci CTXDESC_CD_0_V; 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci /* STALL_MODEL==0b10 && CD.S==0 is ILLEGAL */ 10478c2ecf20Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_STALL_FORCE) 10488c2ecf20Sopenharmony_ci val |= CTXDESC_CD_0_S; 10498c2ecf20Sopenharmony_ci } 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci /* 10528c2ecf20Sopenharmony_ci * The SMMU accesses 64-bit values atomically. See IHI0070Ca 3.21.3 10538c2ecf20Sopenharmony_ci * "Configuration structures and configuration invalidation completion" 10548c2ecf20Sopenharmony_ci * 10558c2ecf20Sopenharmony_ci * The size of single-copy atomic reads made by the SMMU is 10568c2ecf20Sopenharmony_ci * IMPLEMENTATION DEFINED but must be at least 64 bits. Any single 10578c2ecf20Sopenharmony_ci * field within an aligned 64-bit span of a structure can be altered 10588c2ecf20Sopenharmony_ci * without first making the structure invalid. 10598c2ecf20Sopenharmony_ci */ 10608c2ecf20Sopenharmony_ci WRITE_ONCE(cdptr[0], cpu_to_le64(val)); 10618c2ecf20Sopenharmony_ci arm_smmu_sync_cd(smmu_domain, ssid, true); 10628c2ecf20Sopenharmony_ci return 0; 10638c2ecf20Sopenharmony_ci} 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_cistatic int arm_smmu_alloc_cd_tables(struct arm_smmu_domain *smmu_domain) 10668c2ecf20Sopenharmony_ci{ 10678c2ecf20Sopenharmony_ci int ret; 10688c2ecf20Sopenharmony_ci size_t l1size; 10698c2ecf20Sopenharmony_ci size_t max_contexts; 10708c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu = smmu_domain->smmu; 10718c2ecf20Sopenharmony_ci struct arm_smmu_s1_cfg *cfg = &smmu_domain->s1_cfg; 10728c2ecf20Sopenharmony_ci struct arm_smmu_ctx_desc_cfg *cdcfg = &cfg->cdcfg; 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci max_contexts = 1 << cfg->s1cdmax; 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci if (!(smmu->features & ARM_SMMU_FEAT_2_LVL_CDTAB) || 10778c2ecf20Sopenharmony_ci max_contexts <= CTXDESC_L2_ENTRIES) { 10788c2ecf20Sopenharmony_ci cfg->s1fmt = STRTAB_STE_0_S1FMT_LINEAR; 10798c2ecf20Sopenharmony_ci cdcfg->num_l1_ents = max_contexts; 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci l1size = max_contexts * (CTXDESC_CD_DWORDS << 3); 10828c2ecf20Sopenharmony_ci } else { 10838c2ecf20Sopenharmony_ci cfg->s1fmt = STRTAB_STE_0_S1FMT_64K_L2; 10848c2ecf20Sopenharmony_ci cdcfg->num_l1_ents = DIV_ROUND_UP(max_contexts, 10858c2ecf20Sopenharmony_ci CTXDESC_L2_ENTRIES); 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci cdcfg->l1_desc = devm_kcalloc(smmu->dev, cdcfg->num_l1_ents, 10888c2ecf20Sopenharmony_ci sizeof(*cdcfg->l1_desc), 10898c2ecf20Sopenharmony_ci GFP_KERNEL); 10908c2ecf20Sopenharmony_ci if (!cdcfg->l1_desc) 10918c2ecf20Sopenharmony_ci return -ENOMEM; 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci l1size = cdcfg->num_l1_ents * (CTXDESC_L1_DESC_DWORDS << 3); 10948c2ecf20Sopenharmony_ci } 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci cdcfg->cdtab = dmam_alloc_coherent(smmu->dev, l1size, &cdcfg->cdtab_dma, 10978c2ecf20Sopenharmony_ci GFP_KERNEL); 10988c2ecf20Sopenharmony_ci if (!cdcfg->cdtab) { 10998c2ecf20Sopenharmony_ci dev_warn(smmu->dev, "failed to allocate context descriptor\n"); 11008c2ecf20Sopenharmony_ci ret = -ENOMEM; 11018c2ecf20Sopenharmony_ci goto err_free_l1; 11028c2ecf20Sopenharmony_ci } 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci return 0; 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_cierr_free_l1: 11078c2ecf20Sopenharmony_ci if (cdcfg->l1_desc) { 11088c2ecf20Sopenharmony_ci devm_kfree(smmu->dev, cdcfg->l1_desc); 11098c2ecf20Sopenharmony_ci cdcfg->l1_desc = NULL; 11108c2ecf20Sopenharmony_ci } 11118c2ecf20Sopenharmony_ci return ret; 11128c2ecf20Sopenharmony_ci} 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_cistatic void arm_smmu_free_cd_tables(struct arm_smmu_domain *smmu_domain) 11158c2ecf20Sopenharmony_ci{ 11168c2ecf20Sopenharmony_ci int i; 11178c2ecf20Sopenharmony_ci size_t size, l1size; 11188c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu = smmu_domain->smmu; 11198c2ecf20Sopenharmony_ci struct arm_smmu_ctx_desc_cfg *cdcfg = &smmu_domain->s1_cfg.cdcfg; 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci if (cdcfg->l1_desc) { 11228c2ecf20Sopenharmony_ci size = CTXDESC_L2_ENTRIES * (CTXDESC_CD_DWORDS << 3); 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci for (i = 0; i < cdcfg->num_l1_ents; i++) { 11258c2ecf20Sopenharmony_ci if (!cdcfg->l1_desc[i].l2ptr) 11268c2ecf20Sopenharmony_ci continue; 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci dmam_free_coherent(smmu->dev, size, 11298c2ecf20Sopenharmony_ci cdcfg->l1_desc[i].l2ptr, 11308c2ecf20Sopenharmony_ci cdcfg->l1_desc[i].l2ptr_dma); 11318c2ecf20Sopenharmony_ci } 11328c2ecf20Sopenharmony_ci devm_kfree(smmu->dev, cdcfg->l1_desc); 11338c2ecf20Sopenharmony_ci cdcfg->l1_desc = NULL; 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci l1size = cdcfg->num_l1_ents * (CTXDESC_L1_DESC_DWORDS << 3); 11368c2ecf20Sopenharmony_ci } else { 11378c2ecf20Sopenharmony_ci l1size = cdcfg->num_l1_ents * (CTXDESC_CD_DWORDS << 3); 11388c2ecf20Sopenharmony_ci } 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci dmam_free_coherent(smmu->dev, l1size, cdcfg->cdtab, cdcfg->cdtab_dma); 11418c2ecf20Sopenharmony_ci cdcfg->cdtab_dma = 0; 11428c2ecf20Sopenharmony_ci cdcfg->cdtab = NULL; 11438c2ecf20Sopenharmony_ci} 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_cibool arm_smmu_free_asid(struct arm_smmu_ctx_desc *cd) 11468c2ecf20Sopenharmony_ci{ 11478c2ecf20Sopenharmony_ci bool free; 11488c2ecf20Sopenharmony_ci struct arm_smmu_ctx_desc *old_cd; 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci if (!cd->asid) 11518c2ecf20Sopenharmony_ci return false; 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci free = refcount_dec_and_test(&cd->refs); 11548c2ecf20Sopenharmony_ci if (free) { 11558c2ecf20Sopenharmony_ci old_cd = xa_erase(&arm_smmu_asid_xa, cd->asid); 11568c2ecf20Sopenharmony_ci WARN_ON(old_cd != cd); 11578c2ecf20Sopenharmony_ci } 11588c2ecf20Sopenharmony_ci return free; 11598c2ecf20Sopenharmony_ci} 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci/* Stream table manipulation functions */ 11628c2ecf20Sopenharmony_cistatic void 11638c2ecf20Sopenharmony_ciarm_smmu_write_strtab_l1_desc(__le64 *dst, struct arm_smmu_strtab_l1_desc *desc) 11648c2ecf20Sopenharmony_ci{ 11658c2ecf20Sopenharmony_ci u64 val = 0; 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci val |= FIELD_PREP(STRTAB_L1_DESC_SPAN, desc->span); 11688c2ecf20Sopenharmony_ci val |= desc->l2ptr_dma & STRTAB_L1_DESC_L2PTR_MASK; 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci /* See comment in arm_smmu_write_ctx_desc() */ 11718c2ecf20Sopenharmony_ci WRITE_ONCE(*dst, cpu_to_le64(val)); 11728c2ecf20Sopenharmony_ci} 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_cistatic void arm_smmu_sync_ste_for_sid(struct arm_smmu_device *smmu, u32 sid) 11758c2ecf20Sopenharmony_ci{ 11768c2ecf20Sopenharmony_ci struct arm_smmu_cmdq_ent cmd = { 11778c2ecf20Sopenharmony_ci .opcode = CMDQ_OP_CFGI_STE, 11788c2ecf20Sopenharmony_ci .cfgi = { 11798c2ecf20Sopenharmony_ci .sid = sid, 11808c2ecf20Sopenharmony_ci .leaf = true, 11818c2ecf20Sopenharmony_ci }, 11828c2ecf20Sopenharmony_ci }; 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci arm_smmu_cmdq_issue_cmd(smmu, &cmd); 11858c2ecf20Sopenharmony_ci arm_smmu_cmdq_issue_sync(smmu); 11868c2ecf20Sopenharmony_ci} 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_cistatic void arm_smmu_write_strtab_ent(struct arm_smmu_master *master, u32 sid, 11898c2ecf20Sopenharmony_ci __le64 *dst) 11908c2ecf20Sopenharmony_ci{ 11918c2ecf20Sopenharmony_ci /* 11928c2ecf20Sopenharmony_ci * This is hideously complicated, but we only really care about 11938c2ecf20Sopenharmony_ci * three cases at the moment: 11948c2ecf20Sopenharmony_ci * 11958c2ecf20Sopenharmony_ci * 1. Invalid (all zero) -> bypass/fault (init) 11968c2ecf20Sopenharmony_ci * 2. Bypass/fault -> translation/bypass (attach) 11978c2ecf20Sopenharmony_ci * 3. Translation/bypass -> bypass/fault (detach) 11988c2ecf20Sopenharmony_ci * 11998c2ecf20Sopenharmony_ci * Given that we can't update the STE atomically and the SMMU 12008c2ecf20Sopenharmony_ci * doesn't read the thing in a defined order, that leaves us 12018c2ecf20Sopenharmony_ci * with the following maintenance requirements: 12028c2ecf20Sopenharmony_ci * 12038c2ecf20Sopenharmony_ci * 1. Update Config, return (init time STEs aren't live) 12048c2ecf20Sopenharmony_ci * 2. Write everything apart from dword 0, sync, write dword 0, sync 12058c2ecf20Sopenharmony_ci * 3. Update Config, sync 12068c2ecf20Sopenharmony_ci */ 12078c2ecf20Sopenharmony_ci u64 val = le64_to_cpu(dst[0]); 12088c2ecf20Sopenharmony_ci bool ste_live = false; 12098c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu = NULL; 12108c2ecf20Sopenharmony_ci struct arm_smmu_s1_cfg *s1_cfg = NULL; 12118c2ecf20Sopenharmony_ci struct arm_smmu_s2_cfg *s2_cfg = NULL; 12128c2ecf20Sopenharmony_ci struct arm_smmu_domain *smmu_domain = NULL; 12138c2ecf20Sopenharmony_ci struct arm_smmu_cmdq_ent prefetch_cmd = { 12148c2ecf20Sopenharmony_ci .opcode = CMDQ_OP_PREFETCH_CFG, 12158c2ecf20Sopenharmony_ci .prefetch = { 12168c2ecf20Sopenharmony_ci .sid = sid, 12178c2ecf20Sopenharmony_ci }, 12188c2ecf20Sopenharmony_ci }; 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci if (master) { 12218c2ecf20Sopenharmony_ci smmu_domain = master->domain; 12228c2ecf20Sopenharmony_ci smmu = master->smmu; 12238c2ecf20Sopenharmony_ci } 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci if (smmu_domain) { 12268c2ecf20Sopenharmony_ci switch (smmu_domain->stage) { 12278c2ecf20Sopenharmony_ci case ARM_SMMU_DOMAIN_S1: 12288c2ecf20Sopenharmony_ci s1_cfg = &smmu_domain->s1_cfg; 12298c2ecf20Sopenharmony_ci break; 12308c2ecf20Sopenharmony_ci case ARM_SMMU_DOMAIN_S2: 12318c2ecf20Sopenharmony_ci case ARM_SMMU_DOMAIN_NESTED: 12328c2ecf20Sopenharmony_ci s2_cfg = &smmu_domain->s2_cfg; 12338c2ecf20Sopenharmony_ci break; 12348c2ecf20Sopenharmony_ci default: 12358c2ecf20Sopenharmony_ci break; 12368c2ecf20Sopenharmony_ci } 12378c2ecf20Sopenharmony_ci } 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ci if (val & STRTAB_STE_0_V) { 12408c2ecf20Sopenharmony_ci switch (FIELD_GET(STRTAB_STE_0_CFG, val)) { 12418c2ecf20Sopenharmony_ci case STRTAB_STE_0_CFG_BYPASS: 12428c2ecf20Sopenharmony_ci break; 12438c2ecf20Sopenharmony_ci case STRTAB_STE_0_CFG_S1_TRANS: 12448c2ecf20Sopenharmony_ci case STRTAB_STE_0_CFG_S2_TRANS: 12458c2ecf20Sopenharmony_ci ste_live = true; 12468c2ecf20Sopenharmony_ci break; 12478c2ecf20Sopenharmony_ci case STRTAB_STE_0_CFG_ABORT: 12488c2ecf20Sopenharmony_ci BUG_ON(!disable_bypass); 12498c2ecf20Sopenharmony_ci break; 12508c2ecf20Sopenharmony_ci default: 12518c2ecf20Sopenharmony_ci BUG(); /* STE corruption */ 12528c2ecf20Sopenharmony_ci } 12538c2ecf20Sopenharmony_ci } 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_ci /* Nuke the existing STE_0 value, as we're going to rewrite it */ 12568c2ecf20Sopenharmony_ci val = STRTAB_STE_0_V; 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_ci /* Bypass/fault */ 12598c2ecf20Sopenharmony_ci if (!smmu_domain || !(s1_cfg || s2_cfg)) { 12608c2ecf20Sopenharmony_ci if (!smmu_domain && disable_bypass) 12618c2ecf20Sopenharmony_ci val |= FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_ABORT); 12628c2ecf20Sopenharmony_ci else 12638c2ecf20Sopenharmony_ci val |= FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_BYPASS); 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci dst[0] = cpu_to_le64(val); 12668c2ecf20Sopenharmony_ci dst[1] = cpu_to_le64(FIELD_PREP(STRTAB_STE_1_SHCFG, 12678c2ecf20Sopenharmony_ci STRTAB_STE_1_SHCFG_INCOMING)); 12688c2ecf20Sopenharmony_ci dst[2] = 0; /* Nuke the VMID */ 12698c2ecf20Sopenharmony_ci /* 12708c2ecf20Sopenharmony_ci * The SMMU can perform negative caching, so we must sync 12718c2ecf20Sopenharmony_ci * the STE regardless of whether the old value was live. 12728c2ecf20Sopenharmony_ci */ 12738c2ecf20Sopenharmony_ci if (smmu) 12748c2ecf20Sopenharmony_ci arm_smmu_sync_ste_for_sid(smmu, sid); 12758c2ecf20Sopenharmony_ci return; 12768c2ecf20Sopenharmony_ci } 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_ci if (s1_cfg) { 12798c2ecf20Sopenharmony_ci BUG_ON(ste_live); 12808c2ecf20Sopenharmony_ci dst[1] = cpu_to_le64( 12818c2ecf20Sopenharmony_ci FIELD_PREP(STRTAB_STE_1_S1DSS, STRTAB_STE_1_S1DSS_SSID0) | 12828c2ecf20Sopenharmony_ci FIELD_PREP(STRTAB_STE_1_S1CIR, STRTAB_STE_1_S1C_CACHE_WBRA) | 12838c2ecf20Sopenharmony_ci FIELD_PREP(STRTAB_STE_1_S1COR, STRTAB_STE_1_S1C_CACHE_WBRA) | 12848c2ecf20Sopenharmony_ci FIELD_PREP(STRTAB_STE_1_S1CSH, ARM_SMMU_SH_ISH) | 12858c2ecf20Sopenharmony_ci FIELD_PREP(STRTAB_STE_1_STRW, STRTAB_STE_1_STRW_NSEL1)); 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_STALLS && 12888c2ecf20Sopenharmony_ci !(smmu->features & ARM_SMMU_FEAT_STALL_FORCE)) 12898c2ecf20Sopenharmony_ci dst[1] |= cpu_to_le64(STRTAB_STE_1_S1STALLD); 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci val |= (s1_cfg->cdcfg.cdtab_dma & STRTAB_STE_0_S1CTXPTR_MASK) | 12928c2ecf20Sopenharmony_ci FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_S1_TRANS) | 12938c2ecf20Sopenharmony_ci FIELD_PREP(STRTAB_STE_0_S1CDMAX, s1_cfg->s1cdmax) | 12948c2ecf20Sopenharmony_ci FIELD_PREP(STRTAB_STE_0_S1FMT, s1_cfg->s1fmt); 12958c2ecf20Sopenharmony_ci } 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_ci if (s2_cfg) { 12988c2ecf20Sopenharmony_ci BUG_ON(ste_live); 12998c2ecf20Sopenharmony_ci dst[2] = cpu_to_le64( 13008c2ecf20Sopenharmony_ci FIELD_PREP(STRTAB_STE_2_S2VMID, s2_cfg->vmid) | 13018c2ecf20Sopenharmony_ci FIELD_PREP(STRTAB_STE_2_VTCR, s2_cfg->vtcr) | 13028c2ecf20Sopenharmony_ci#ifdef __BIG_ENDIAN 13038c2ecf20Sopenharmony_ci STRTAB_STE_2_S2ENDI | 13048c2ecf20Sopenharmony_ci#endif 13058c2ecf20Sopenharmony_ci STRTAB_STE_2_S2PTW | STRTAB_STE_2_S2AA64 | 13068c2ecf20Sopenharmony_ci STRTAB_STE_2_S2R); 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci dst[3] = cpu_to_le64(s2_cfg->vttbr & STRTAB_STE_3_S2TTB_MASK); 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_ci val |= FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_S2_TRANS); 13118c2ecf20Sopenharmony_ci } 13128c2ecf20Sopenharmony_ci 13138c2ecf20Sopenharmony_ci if (master->ats_enabled) 13148c2ecf20Sopenharmony_ci dst[1] |= cpu_to_le64(FIELD_PREP(STRTAB_STE_1_EATS, 13158c2ecf20Sopenharmony_ci STRTAB_STE_1_EATS_TRANS)); 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci arm_smmu_sync_ste_for_sid(smmu, sid); 13188c2ecf20Sopenharmony_ci /* See comment in arm_smmu_write_ctx_desc() */ 13198c2ecf20Sopenharmony_ci WRITE_ONCE(dst[0], cpu_to_le64(val)); 13208c2ecf20Sopenharmony_ci arm_smmu_sync_ste_for_sid(smmu, sid); 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci /* It's likely that we'll want to use the new STE soon */ 13238c2ecf20Sopenharmony_ci if (!(smmu->options & ARM_SMMU_OPT_SKIP_PREFETCH)) 13248c2ecf20Sopenharmony_ci arm_smmu_cmdq_issue_cmd(smmu, &prefetch_cmd); 13258c2ecf20Sopenharmony_ci} 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_cistatic void arm_smmu_init_bypass_stes(__le64 *strtab, unsigned int nent) 13288c2ecf20Sopenharmony_ci{ 13298c2ecf20Sopenharmony_ci unsigned int i; 13308c2ecf20Sopenharmony_ci 13318c2ecf20Sopenharmony_ci for (i = 0; i < nent; ++i) { 13328c2ecf20Sopenharmony_ci arm_smmu_write_strtab_ent(NULL, -1, strtab); 13338c2ecf20Sopenharmony_ci strtab += STRTAB_STE_DWORDS; 13348c2ecf20Sopenharmony_ci } 13358c2ecf20Sopenharmony_ci} 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_cistatic int arm_smmu_init_l2_strtab(struct arm_smmu_device *smmu, u32 sid) 13388c2ecf20Sopenharmony_ci{ 13398c2ecf20Sopenharmony_ci size_t size; 13408c2ecf20Sopenharmony_ci void *strtab; 13418c2ecf20Sopenharmony_ci struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg; 13428c2ecf20Sopenharmony_ci struct arm_smmu_strtab_l1_desc *desc = &cfg->l1_desc[sid >> STRTAB_SPLIT]; 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_ci if (desc->l2ptr) 13458c2ecf20Sopenharmony_ci return 0; 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_ci size = 1 << (STRTAB_SPLIT + ilog2(STRTAB_STE_DWORDS) + 3); 13488c2ecf20Sopenharmony_ci strtab = &cfg->strtab[(sid >> STRTAB_SPLIT) * STRTAB_L1_DESC_DWORDS]; 13498c2ecf20Sopenharmony_ci 13508c2ecf20Sopenharmony_ci desc->span = STRTAB_SPLIT + 1; 13518c2ecf20Sopenharmony_ci desc->l2ptr = dmam_alloc_coherent(smmu->dev, size, &desc->l2ptr_dma, 13528c2ecf20Sopenharmony_ci GFP_KERNEL); 13538c2ecf20Sopenharmony_ci if (!desc->l2ptr) { 13548c2ecf20Sopenharmony_ci dev_err(smmu->dev, 13558c2ecf20Sopenharmony_ci "failed to allocate l2 stream table for SID %u\n", 13568c2ecf20Sopenharmony_ci sid); 13578c2ecf20Sopenharmony_ci return -ENOMEM; 13588c2ecf20Sopenharmony_ci } 13598c2ecf20Sopenharmony_ci 13608c2ecf20Sopenharmony_ci arm_smmu_init_bypass_stes(desc->l2ptr, 1 << STRTAB_SPLIT); 13618c2ecf20Sopenharmony_ci arm_smmu_write_strtab_l1_desc(strtab, desc); 13628c2ecf20Sopenharmony_ci return 0; 13638c2ecf20Sopenharmony_ci} 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_ci/* IRQ and event handlers */ 13668c2ecf20Sopenharmony_cistatic irqreturn_t arm_smmu_evtq_thread(int irq, void *dev) 13678c2ecf20Sopenharmony_ci{ 13688c2ecf20Sopenharmony_ci int i; 13698c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu = dev; 13708c2ecf20Sopenharmony_ci struct arm_smmu_queue *q = &smmu->evtq.q; 13718c2ecf20Sopenharmony_ci struct arm_smmu_ll_queue *llq = &q->llq; 13728c2ecf20Sopenharmony_ci u64 evt[EVTQ_ENT_DWORDS]; 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ci do { 13758c2ecf20Sopenharmony_ci while (!queue_remove_raw(q, evt)) { 13768c2ecf20Sopenharmony_ci u8 id = FIELD_GET(EVTQ_0_ID, evt[0]); 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci dev_info(smmu->dev, "event 0x%02x received:\n", id); 13798c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(evt); ++i) 13808c2ecf20Sopenharmony_ci dev_info(smmu->dev, "\t0x%016llx\n", 13818c2ecf20Sopenharmony_ci (unsigned long long)evt[i]); 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_ci cond_resched(); 13848c2ecf20Sopenharmony_ci } 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_ci /* 13878c2ecf20Sopenharmony_ci * Not much we can do on overflow, so scream and pretend we're 13888c2ecf20Sopenharmony_ci * trying harder. 13898c2ecf20Sopenharmony_ci */ 13908c2ecf20Sopenharmony_ci if (queue_sync_prod_in(q) == -EOVERFLOW) 13918c2ecf20Sopenharmony_ci dev_err(smmu->dev, "EVTQ overflow detected -- events lost\n"); 13928c2ecf20Sopenharmony_ci } while (!queue_empty(llq)); 13938c2ecf20Sopenharmony_ci 13948c2ecf20Sopenharmony_ci /* Sync our overflow flag, as we believe we're up to speed */ 13958c2ecf20Sopenharmony_ci queue_sync_cons_ovf(q); 13968c2ecf20Sopenharmony_ci return IRQ_HANDLED; 13978c2ecf20Sopenharmony_ci} 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_cistatic void arm_smmu_handle_ppr(struct arm_smmu_device *smmu, u64 *evt) 14008c2ecf20Sopenharmony_ci{ 14018c2ecf20Sopenharmony_ci u32 sid, ssid; 14028c2ecf20Sopenharmony_ci u16 grpid; 14038c2ecf20Sopenharmony_ci bool ssv, last; 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_ci sid = FIELD_GET(PRIQ_0_SID, evt[0]); 14068c2ecf20Sopenharmony_ci ssv = FIELD_GET(PRIQ_0_SSID_V, evt[0]); 14078c2ecf20Sopenharmony_ci ssid = ssv ? FIELD_GET(PRIQ_0_SSID, evt[0]) : 0; 14088c2ecf20Sopenharmony_ci last = FIELD_GET(PRIQ_0_PRG_LAST, evt[0]); 14098c2ecf20Sopenharmony_ci grpid = FIELD_GET(PRIQ_1_PRG_IDX, evt[1]); 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_ci dev_info(smmu->dev, "unexpected PRI request received:\n"); 14128c2ecf20Sopenharmony_ci dev_info(smmu->dev, 14138c2ecf20Sopenharmony_ci "\tsid 0x%08x.0x%05x: [%u%s] %sprivileged %s%s%s access at iova 0x%016llx\n", 14148c2ecf20Sopenharmony_ci sid, ssid, grpid, last ? "L" : "", 14158c2ecf20Sopenharmony_ci evt[0] & PRIQ_0_PERM_PRIV ? "" : "un", 14168c2ecf20Sopenharmony_ci evt[0] & PRIQ_0_PERM_READ ? "R" : "", 14178c2ecf20Sopenharmony_ci evt[0] & PRIQ_0_PERM_WRITE ? "W" : "", 14188c2ecf20Sopenharmony_ci evt[0] & PRIQ_0_PERM_EXEC ? "X" : "", 14198c2ecf20Sopenharmony_ci evt[1] & PRIQ_1_ADDR_MASK); 14208c2ecf20Sopenharmony_ci 14218c2ecf20Sopenharmony_ci if (last) { 14228c2ecf20Sopenharmony_ci struct arm_smmu_cmdq_ent cmd = { 14238c2ecf20Sopenharmony_ci .opcode = CMDQ_OP_PRI_RESP, 14248c2ecf20Sopenharmony_ci .substream_valid = ssv, 14258c2ecf20Sopenharmony_ci .pri = { 14268c2ecf20Sopenharmony_ci .sid = sid, 14278c2ecf20Sopenharmony_ci .ssid = ssid, 14288c2ecf20Sopenharmony_ci .grpid = grpid, 14298c2ecf20Sopenharmony_ci .resp = PRI_RESP_DENY, 14308c2ecf20Sopenharmony_ci }, 14318c2ecf20Sopenharmony_ci }; 14328c2ecf20Sopenharmony_ci 14338c2ecf20Sopenharmony_ci arm_smmu_cmdq_issue_cmd(smmu, &cmd); 14348c2ecf20Sopenharmony_ci } 14358c2ecf20Sopenharmony_ci} 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_cistatic irqreturn_t arm_smmu_priq_thread(int irq, void *dev) 14388c2ecf20Sopenharmony_ci{ 14398c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu = dev; 14408c2ecf20Sopenharmony_ci struct arm_smmu_queue *q = &smmu->priq.q; 14418c2ecf20Sopenharmony_ci struct arm_smmu_ll_queue *llq = &q->llq; 14428c2ecf20Sopenharmony_ci u64 evt[PRIQ_ENT_DWORDS]; 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_ci do { 14458c2ecf20Sopenharmony_ci while (!queue_remove_raw(q, evt)) 14468c2ecf20Sopenharmony_ci arm_smmu_handle_ppr(smmu, evt); 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci if (queue_sync_prod_in(q) == -EOVERFLOW) 14498c2ecf20Sopenharmony_ci dev_err(smmu->dev, "PRIQ overflow detected -- requests lost\n"); 14508c2ecf20Sopenharmony_ci } while (!queue_empty(llq)); 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_ci /* Sync our overflow flag, as we believe we're up to speed */ 14538c2ecf20Sopenharmony_ci queue_sync_cons_ovf(q); 14548c2ecf20Sopenharmony_ci return IRQ_HANDLED; 14558c2ecf20Sopenharmony_ci} 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_cistatic int arm_smmu_device_disable(struct arm_smmu_device *smmu); 14588c2ecf20Sopenharmony_ci 14598c2ecf20Sopenharmony_cistatic irqreturn_t arm_smmu_gerror_handler(int irq, void *dev) 14608c2ecf20Sopenharmony_ci{ 14618c2ecf20Sopenharmony_ci u32 gerror, gerrorn, active; 14628c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu = dev; 14638c2ecf20Sopenharmony_ci 14648c2ecf20Sopenharmony_ci gerror = readl_relaxed(smmu->base + ARM_SMMU_GERROR); 14658c2ecf20Sopenharmony_ci gerrorn = readl_relaxed(smmu->base + ARM_SMMU_GERRORN); 14668c2ecf20Sopenharmony_ci 14678c2ecf20Sopenharmony_ci active = gerror ^ gerrorn; 14688c2ecf20Sopenharmony_ci if (!(active & GERROR_ERR_MASK)) 14698c2ecf20Sopenharmony_ci return IRQ_NONE; /* No errors pending */ 14708c2ecf20Sopenharmony_ci 14718c2ecf20Sopenharmony_ci dev_warn(smmu->dev, 14728c2ecf20Sopenharmony_ci "unexpected global error reported (0x%08x), this could be serious\n", 14738c2ecf20Sopenharmony_ci active); 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_ci if (active & GERROR_SFM_ERR) { 14768c2ecf20Sopenharmony_ci dev_err(smmu->dev, "device has entered Service Failure Mode!\n"); 14778c2ecf20Sopenharmony_ci arm_smmu_device_disable(smmu); 14788c2ecf20Sopenharmony_ci } 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci if (active & GERROR_MSI_GERROR_ABT_ERR) 14818c2ecf20Sopenharmony_ci dev_warn(smmu->dev, "GERROR MSI write aborted\n"); 14828c2ecf20Sopenharmony_ci 14838c2ecf20Sopenharmony_ci if (active & GERROR_MSI_PRIQ_ABT_ERR) 14848c2ecf20Sopenharmony_ci dev_warn(smmu->dev, "PRIQ MSI write aborted\n"); 14858c2ecf20Sopenharmony_ci 14868c2ecf20Sopenharmony_ci if (active & GERROR_MSI_EVTQ_ABT_ERR) 14878c2ecf20Sopenharmony_ci dev_warn(smmu->dev, "EVTQ MSI write aborted\n"); 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_ci if (active & GERROR_MSI_CMDQ_ABT_ERR) 14908c2ecf20Sopenharmony_ci dev_warn(smmu->dev, "CMDQ MSI write aborted\n"); 14918c2ecf20Sopenharmony_ci 14928c2ecf20Sopenharmony_ci if (active & GERROR_PRIQ_ABT_ERR) 14938c2ecf20Sopenharmony_ci dev_err(smmu->dev, "PRIQ write aborted -- events may have been lost\n"); 14948c2ecf20Sopenharmony_ci 14958c2ecf20Sopenharmony_ci if (active & GERROR_EVTQ_ABT_ERR) 14968c2ecf20Sopenharmony_ci dev_err(smmu->dev, "EVTQ write aborted -- events may have been lost\n"); 14978c2ecf20Sopenharmony_ci 14988c2ecf20Sopenharmony_ci if (active & GERROR_CMDQ_ERR) 14998c2ecf20Sopenharmony_ci arm_smmu_cmdq_skip_err(smmu); 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci writel(gerror, smmu->base + ARM_SMMU_GERRORN); 15028c2ecf20Sopenharmony_ci return IRQ_HANDLED; 15038c2ecf20Sopenharmony_ci} 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_cistatic irqreturn_t arm_smmu_combined_irq_thread(int irq, void *dev) 15068c2ecf20Sopenharmony_ci{ 15078c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu = dev; 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_ci arm_smmu_evtq_thread(irq, dev); 15108c2ecf20Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_PRI) 15118c2ecf20Sopenharmony_ci arm_smmu_priq_thread(irq, dev); 15128c2ecf20Sopenharmony_ci 15138c2ecf20Sopenharmony_ci return IRQ_HANDLED; 15148c2ecf20Sopenharmony_ci} 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_cistatic irqreturn_t arm_smmu_combined_irq_handler(int irq, void *dev) 15178c2ecf20Sopenharmony_ci{ 15188c2ecf20Sopenharmony_ci arm_smmu_gerror_handler(irq, dev); 15198c2ecf20Sopenharmony_ci return IRQ_WAKE_THREAD; 15208c2ecf20Sopenharmony_ci} 15218c2ecf20Sopenharmony_ci 15228c2ecf20Sopenharmony_cistatic void 15238c2ecf20Sopenharmony_ciarm_smmu_atc_inv_to_cmd(int ssid, unsigned long iova, size_t size, 15248c2ecf20Sopenharmony_ci struct arm_smmu_cmdq_ent *cmd) 15258c2ecf20Sopenharmony_ci{ 15268c2ecf20Sopenharmony_ci size_t log2_span; 15278c2ecf20Sopenharmony_ci size_t span_mask; 15288c2ecf20Sopenharmony_ci /* ATC invalidates are always on 4096-bytes pages */ 15298c2ecf20Sopenharmony_ci size_t inval_grain_shift = 12; 15308c2ecf20Sopenharmony_ci unsigned long page_start, page_end; 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_ci *cmd = (struct arm_smmu_cmdq_ent) { 15338c2ecf20Sopenharmony_ci .opcode = CMDQ_OP_ATC_INV, 15348c2ecf20Sopenharmony_ci .substream_valid = !!ssid, 15358c2ecf20Sopenharmony_ci .atc.ssid = ssid, 15368c2ecf20Sopenharmony_ci }; 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_ci if (!size) { 15398c2ecf20Sopenharmony_ci cmd->atc.size = ATC_INV_SIZE_ALL; 15408c2ecf20Sopenharmony_ci return; 15418c2ecf20Sopenharmony_ci } 15428c2ecf20Sopenharmony_ci 15438c2ecf20Sopenharmony_ci page_start = iova >> inval_grain_shift; 15448c2ecf20Sopenharmony_ci page_end = (iova + size - 1) >> inval_grain_shift; 15458c2ecf20Sopenharmony_ci 15468c2ecf20Sopenharmony_ci /* 15478c2ecf20Sopenharmony_ci * In an ATS Invalidate Request, the address must be aligned on the 15488c2ecf20Sopenharmony_ci * range size, which must be a power of two number of page sizes. We 15498c2ecf20Sopenharmony_ci * thus have to choose between grossly over-invalidating the region, or 15508c2ecf20Sopenharmony_ci * splitting the invalidation into multiple commands. For simplicity 15518c2ecf20Sopenharmony_ci * we'll go with the first solution, but should refine it in the future 15528c2ecf20Sopenharmony_ci * if multiple commands are shown to be more efficient. 15538c2ecf20Sopenharmony_ci * 15548c2ecf20Sopenharmony_ci * Find the smallest power of two that covers the range. The most 15558c2ecf20Sopenharmony_ci * significant differing bit between the start and end addresses, 15568c2ecf20Sopenharmony_ci * fls(start ^ end), indicates the required span. For example: 15578c2ecf20Sopenharmony_ci * 15588c2ecf20Sopenharmony_ci * We want to invalidate pages [8; 11]. This is already the ideal range: 15598c2ecf20Sopenharmony_ci * x = 0b1000 ^ 0b1011 = 0b11 15608c2ecf20Sopenharmony_ci * span = 1 << fls(x) = 4 15618c2ecf20Sopenharmony_ci * 15628c2ecf20Sopenharmony_ci * To invalidate pages [7; 10], we need to invalidate [0; 15]: 15638c2ecf20Sopenharmony_ci * x = 0b0111 ^ 0b1010 = 0b1101 15648c2ecf20Sopenharmony_ci * span = 1 << fls(x) = 16 15658c2ecf20Sopenharmony_ci */ 15668c2ecf20Sopenharmony_ci log2_span = fls_long(page_start ^ page_end); 15678c2ecf20Sopenharmony_ci span_mask = (1ULL << log2_span) - 1; 15688c2ecf20Sopenharmony_ci 15698c2ecf20Sopenharmony_ci page_start &= ~span_mask; 15708c2ecf20Sopenharmony_ci 15718c2ecf20Sopenharmony_ci cmd->atc.addr = page_start << inval_grain_shift; 15728c2ecf20Sopenharmony_ci cmd->atc.size = log2_span; 15738c2ecf20Sopenharmony_ci} 15748c2ecf20Sopenharmony_ci 15758c2ecf20Sopenharmony_cistatic int arm_smmu_atc_inv_master(struct arm_smmu_master *master) 15768c2ecf20Sopenharmony_ci{ 15778c2ecf20Sopenharmony_ci int i; 15788c2ecf20Sopenharmony_ci struct arm_smmu_cmdq_ent cmd; 15798c2ecf20Sopenharmony_ci 15808c2ecf20Sopenharmony_ci arm_smmu_atc_inv_to_cmd(0, 0, 0, &cmd); 15818c2ecf20Sopenharmony_ci 15828c2ecf20Sopenharmony_ci for (i = 0; i < master->num_sids; i++) { 15838c2ecf20Sopenharmony_ci cmd.atc.sid = master->sids[i]; 15848c2ecf20Sopenharmony_ci arm_smmu_cmdq_issue_cmd(master->smmu, &cmd); 15858c2ecf20Sopenharmony_ci } 15868c2ecf20Sopenharmony_ci 15878c2ecf20Sopenharmony_ci return arm_smmu_cmdq_issue_sync(master->smmu); 15888c2ecf20Sopenharmony_ci} 15898c2ecf20Sopenharmony_ci 15908c2ecf20Sopenharmony_cistatic int arm_smmu_atc_inv_domain(struct arm_smmu_domain *smmu_domain, 15918c2ecf20Sopenharmony_ci int ssid, unsigned long iova, size_t size) 15928c2ecf20Sopenharmony_ci{ 15938c2ecf20Sopenharmony_ci int i; 15948c2ecf20Sopenharmony_ci unsigned long flags; 15958c2ecf20Sopenharmony_ci struct arm_smmu_cmdq_ent cmd; 15968c2ecf20Sopenharmony_ci struct arm_smmu_master *master; 15978c2ecf20Sopenharmony_ci struct arm_smmu_cmdq_batch cmds = {}; 15988c2ecf20Sopenharmony_ci 15998c2ecf20Sopenharmony_ci if (!(smmu_domain->smmu->features & ARM_SMMU_FEAT_ATS)) 16008c2ecf20Sopenharmony_ci return 0; 16018c2ecf20Sopenharmony_ci 16028c2ecf20Sopenharmony_ci /* 16038c2ecf20Sopenharmony_ci * Ensure that we've completed prior invalidation of the main TLBs 16048c2ecf20Sopenharmony_ci * before we read 'nr_ats_masters' in case of a concurrent call to 16058c2ecf20Sopenharmony_ci * arm_smmu_enable_ats(): 16068c2ecf20Sopenharmony_ci * 16078c2ecf20Sopenharmony_ci * // unmap() // arm_smmu_enable_ats() 16088c2ecf20Sopenharmony_ci * TLBI+SYNC atomic_inc(&nr_ats_masters); 16098c2ecf20Sopenharmony_ci * smp_mb(); [...] 16108c2ecf20Sopenharmony_ci * atomic_read(&nr_ats_masters); pci_enable_ats() // writel() 16118c2ecf20Sopenharmony_ci * 16128c2ecf20Sopenharmony_ci * Ensures that we always see the incremented 'nr_ats_masters' count if 16138c2ecf20Sopenharmony_ci * ATS was enabled at the PCI device before completion of the TLBI. 16148c2ecf20Sopenharmony_ci */ 16158c2ecf20Sopenharmony_ci smp_mb(); 16168c2ecf20Sopenharmony_ci if (!atomic_read(&smmu_domain->nr_ats_masters)) 16178c2ecf20Sopenharmony_ci return 0; 16188c2ecf20Sopenharmony_ci 16198c2ecf20Sopenharmony_ci arm_smmu_atc_inv_to_cmd(ssid, iova, size, &cmd); 16208c2ecf20Sopenharmony_ci 16218c2ecf20Sopenharmony_ci spin_lock_irqsave(&smmu_domain->devices_lock, flags); 16228c2ecf20Sopenharmony_ci list_for_each_entry(master, &smmu_domain->devices, domain_head) { 16238c2ecf20Sopenharmony_ci if (!master->ats_enabled) 16248c2ecf20Sopenharmony_ci continue; 16258c2ecf20Sopenharmony_ci 16268c2ecf20Sopenharmony_ci for (i = 0; i < master->num_sids; i++) { 16278c2ecf20Sopenharmony_ci cmd.atc.sid = master->sids[i]; 16288c2ecf20Sopenharmony_ci arm_smmu_cmdq_batch_add(smmu_domain->smmu, &cmds, &cmd); 16298c2ecf20Sopenharmony_ci } 16308c2ecf20Sopenharmony_ci } 16318c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&smmu_domain->devices_lock, flags); 16328c2ecf20Sopenharmony_ci 16338c2ecf20Sopenharmony_ci return arm_smmu_cmdq_batch_submit(smmu_domain->smmu, &cmds); 16348c2ecf20Sopenharmony_ci} 16358c2ecf20Sopenharmony_ci 16368c2ecf20Sopenharmony_ci/* IO_PGTABLE API */ 16378c2ecf20Sopenharmony_cistatic void arm_smmu_tlb_inv_context(void *cookie) 16388c2ecf20Sopenharmony_ci{ 16398c2ecf20Sopenharmony_ci struct arm_smmu_domain *smmu_domain = cookie; 16408c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu = smmu_domain->smmu; 16418c2ecf20Sopenharmony_ci struct arm_smmu_cmdq_ent cmd; 16428c2ecf20Sopenharmony_ci 16438c2ecf20Sopenharmony_ci /* 16448c2ecf20Sopenharmony_ci * NOTE: when io-pgtable is in non-strict mode, we may get here with 16458c2ecf20Sopenharmony_ci * PTEs previously cleared by unmaps on the current CPU not yet visible 16468c2ecf20Sopenharmony_ci * to the SMMU. We are relying on the dma_wmb() implicit during cmd 16478c2ecf20Sopenharmony_ci * insertion to guarantee those are observed before the TLBI. Do be 16488c2ecf20Sopenharmony_ci * careful, 007. 16498c2ecf20Sopenharmony_ci */ 16508c2ecf20Sopenharmony_ci if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) { 16518c2ecf20Sopenharmony_ci arm_smmu_tlb_inv_asid(smmu, smmu_domain->s1_cfg.cd.asid); 16528c2ecf20Sopenharmony_ci } else { 16538c2ecf20Sopenharmony_ci cmd.opcode = CMDQ_OP_TLBI_S12_VMALL; 16548c2ecf20Sopenharmony_ci cmd.tlbi.vmid = smmu_domain->s2_cfg.vmid; 16558c2ecf20Sopenharmony_ci arm_smmu_cmdq_issue_cmd(smmu, &cmd); 16568c2ecf20Sopenharmony_ci arm_smmu_cmdq_issue_sync(smmu); 16578c2ecf20Sopenharmony_ci } 16588c2ecf20Sopenharmony_ci arm_smmu_atc_inv_domain(smmu_domain, 0, 0, 0); 16598c2ecf20Sopenharmony_ci} 16608c2ecf20Sopenharmony_ci 16618c2ecf20Sopenharmony_cistatic void arm_smmu_tlb_inv_range(unsigned long iova, size_t size, 16628c2ecf20Sopenharmony_ci size_t granule, bool leaf, 16638c2ecf20Sopenharmony_ci struct arm_smmu_domain *smmu_domain) 16648c2ecf20Sopenharmony_ci{ 16658c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu = smmu_domain->smmu; 16668c2ecf20Sopenharmony_ci unsigned long start = iova, end = iova + size, num_pages = 0, tg = 0; 16678c2ecf20Sopenharmony_ci size_t inv_range = granule; 16688c2ecf20Sopenharmony_ci struct arm_smmu_cmdq_batch cmds = {}; 16698c2ecf20Sopenharmony_ci struct arm_smmu_cmdq_ent cmd = { 16708c2ecf20Sopenharmony_ci .tlbi = { 16718c2ecf20Sopenharmony_ci .leaf = leaf, 16728c2ecf20Sopenharmony_ci }, 16738c2ecf20Sopenharmony_ci }; 16748c2ecf20Sopenharmony_ci 16758c2ecf20Sopenharmony_ci if (!size) 16768c2ecf20Sopenharmony_ci return; 16778c2ecf20Sopenharmony_ci 16788c2ecf20Sopenharmony_ci if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) { 16798c2ecf20Sopenharmony_ci cmd.opcode = CMDQ_OP_TLBI_NH_VA; 16808c2ecf20Sopenharmony_ci cmd.tlbi.asid = smmu_domain->s1_cfg.cd.asid; 16818c2ecf20Sopenharmony_ci } else { 16828c2ecf20Sopenharmony_ci cmd.opcode = CMDQ_OP_TLBI_S2_IPA; 16838c2ecf20Sopenharmony_ci cmd.tlbi.vmid = smmu_domain->s2_cfg.vmid; 16848c2ecf20Sopenharmony_ci } 16858c2ecf20Sopenharmony_ci 16868c2ecf20Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_RANGE_INV) { 16878c2ecf20Sopenharmony_ci /* Get the leaf page size */ 16888c2ecf20Sopenharmony_ci tg = __ffs(smmu_domain->domain.pgsize_bitmap); 16898c2ecf20Sopenharmony_ci 16908c2ecf20Sopenharmony_ci /* Convert page size of 12,14,16 (log2) to 1,2,3 */ 16918c2ecf20Sopenharmony_ci cmd.tlbi.tg = (tg - 10) / 2; 16928c2ecf20Sopenharmony_ci 16938c2ecf20Sopenharmony_ci /* Determine what level the granule is at */ 16948c2ecf20Sopenharmony_ci cmd.tlbi.ttl = 4 - ((ilog2(granule) - 3) / (tg - 3)); 16958c2ecf20Sopenharmony_ci 16968c2ecf20Sopenharmony_ci num_pages = size >> tg; 16978c2ecf20Sopenharmony_ci } 16988c2ecf20Sopenharmony_ci 16998c2ecf20Sopenharmony_ci while (iova < end) { 17008c2ecf20Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_RANGE_INV) { 17018c2ecf20Sopenharmony_ci /* 17028c2ecf20Sopenharmony_ci * On each iteration of the loop, the range is 5 bits 17038c2ecf20Sopenharmony_ci * worth of the aligned size remaining. 17048c2ecf20Sopenharmony_ci * The range in pages is: 17058c2ecf20Sopenharmony_ci * 17068c2ecf20Sopenharmony_ci * range = (num_pages & (0x1f << __ffs(num_pages))) 17078c2ecf20Sopenharmony_ci */ 17088c2ecf20Sopenharmony_ci unsigned long scale, num; 17098c2ecf20Sopenharmony_ci 17108c2ecf20Sopenharmony_ci /* Determine the power of 2 multiple number of pages */ 17118c2ecf20Sopenharmony_ci scale = __ffs(num_pages); 17128c2ecf20Sopenharmony_ci cmd.tlbi.scale = scale; 17138c2ecf20Sopenharmony_ci 17148c2ecf20Sopenharmony_ci /* Determine how many chunks of 2^scale size we have */ 17158c2ecf20Sopenharmony_ci num = (num_pages >> scale) & CMDQ_TLBI_RANGE_NUM_MAX; 17168c2ecf20Sopenharmony_ci cmd.tlbi.num = num - 1; 17178c2ecf20Sopenharmony_ci 17188c2ecf20Sopenharmony_ci /* range is num * 2^scale * pgsize */ 17198c2ecf20Sopenharmony_ci inv_range = num << (scale + tg); 17208c2ecf20Sopenharmony_ci 17218c2ecf20Sopenharmony_ci /* Clear out the lower order bits for the next iteration */ 17228c2ecf20Sopenharmony_ci num_pages -= num << scale; 17238c2ecf20Sopenharmony_ci } 17248c2ecf20Sopenharmony_ci 17258c2ecf20Sopenharmony_ci cmd.tlbi.addr = iova; 17268c2ecf20Sopenharmony_ci arm_smmu_cmdq_batch_add(smmu, &cmds, &cmd); 17278c2ecf20Sopenharmony_ci iova += inv_range; 17288c2ecf20Sopenharmony_ci } 17298c2ecf20Sopenharmony_ci arm_smmu_cmdq_batch_submit(smmu, &cmds); 17308c2ecf20Sopenharmony_ci 17318c2ecf20Sopenharmony_ci /* 17328c2ecf20Sopenharmony_ci * Unfortunately, this can't be leaf-only since we may have 17338c2ecf20Sopenharmony_ci * zapped an entire table. 17348c2ecf20Sopenharmony_ci */ 17358c2ecf20Sopenharmony_ci arm_smmu_atc_inv_domain(smmu_domain, 0, start, size); 17368c2ecf20Sopenharmony_ci} 17378c2ecf20Sopenharmony_ci 17388c2ecf20Sopenharmony_cistatic void arm_smmu_tlb_inv_page_nosync(struct iommu_iotlb_gather *gather, 17398c2ecf20Sopenharmony_ci unsigned long iova, size_t granule, 17408c2ecf20Sopenharmony_ci void *cookie) 17418c2ecf20Sopenharmony_ci{ 17428c2ecf20Sopenharmony_ci struct arm_smmu_domain *smmu_domain = cookie; 17438c2ecf20Sopenharmony_ci struct iommu_domain *domain = &smmu_domain->domain; 17448c2ecf20Sopenharmony_ci 17458c2ecf20Sopenharmony_ci iommu_iotlb_gather_add_page(domain, gather, iova, granule); 17468c2ecf20Sopenharmony_ci} 17478c2ecf20Sopenharmony_ci 17488c2ecf20Sopenharmony_cistatic void arm_smmu_tlb_inv_walk(unsigned long iova, size_t size, 17498c2ecf20Sopenharmony_ci size_t granule, void *cookie) 17508c2ecf20Sopenharmony_ci{ 17518c2ecf20Sopenharmony_ci arm_smmu_tlb_inv_range(iova, size, granule, false, cookie); 17528c2ecf20Sopenharmony_ci} 17538c2ecf20Sopenharmony_ci 17548c2ecf20Sopenharmony_cistatic void arm_smmu_tlb_inv_leaf(unsigned long iova, size_t size, 17558c2ecf20Sopenharmony_ci size_t granule, void *cookie) 17568c2ecf20Sopenharmony_ci{ 17578c2ecf20Sopenharmony_ci arm_smmu_tlb_inv_range(iova, size, granule, true, cookie); 17588c2ecf20Sopenharmony_ci} 17598c2ecf20Sopenharmony_ci 17608c2ecf20Sopenharmony_cistatic const struct iommu_flush_ops arm_smmu_flush_ops = { 17618c2ecf20Sopenharmony_ci .tlb_flush_all = arm_smmu_tlb_inv_context, 17628c2ecf20Sopenharmony_ci .tlb_flush_walk = arm_smmu_tlb_inv_walk, 17638c2ecf20Sopenharmony_ci .tlb_flush_leaf = arm_smmu_tlb_inv_leaf, 17648c2ecf20Sopenharmony_ci .tlb_add_page = arm_smmu_tlb_inv_page_nosync, 17658c2ecf20Sopenharmony_ci}; 17668c2ecf20Sopenharmony_ci 17678c2ecf20Sopenharmony_ci/* IOMMU API */ 17688c2ecf20Sopenharmony_cistatic bool arm_smmu_capable(enum iommu_cap cap) 17698c2ecf20Sopenharmony_ci{ 17708c2ecf20Sopenharmony_ci switch (cap) { 17718c2ecf20Sopenharmony_ci case IOMMU_CAP_CACHE_COHERENCY: 17728c2ecf20Sopenharmony_ci return true; 17738c2ecf20Sopenharmony_ci case IOMMU_CAP_NOEXEC: 17748c2ecf20Sopenharmony_ci return true; 17758c2ecf20Sopenharmony_ci default: 17768c2ecf20Sopenharmony_ci return false; 17778c2ecf20Sopenharmony_ci } 17788c2ecf20Sopenharmony_ci} 17798c2ecf20Sopenharmony_ci 17808c2ecf20Sopenharmony_cistatic struct iommu_domain *arm_smmu_domain_alloc(unsigned type) 17818c2ecf20Sopenharmony_ci{ 17828c2ecf20Sopenharmony_ci struct arm_smmu_domain *smmu_domain; 17838c2ecf20Sopenharmony_ci 17848c2ecf20Sopenharmony_ci if (type != IOMMU_DOMAIN_UNMANAGED && 17858c2ecf20Sopenharmony_ci type != IOMMU_DOMAIN_DMA && 17868c2ecf20Sopenharmony_ci type != IOMMU_DOMAIN_IDENTITY) 17878c2ecf20Sopenharmony_ci return NULL; 17888c2ecf20Sopenharmony_ci 17898c2ecf20Sopenharmony_ci /* 17908c2ecf20Sopenharmony_ci * Allocate the domain and initialise some of its data structures. 17918c2ecf20Sopenharmony_ci * We can't really do anything meaningful until we've added a 17928c2ecf20Sopenharmony_ci * master. 17938c2ecf20Sopenharmony_ci */ 17948c2ecf20Sopenharmony_ci smmu_domain = kzalloc(sizeof(*smmu_domain), GFP_KERNEL); 17958c2ecf20Sopenharmony_ci if (!smmu_domain) 17968c2ecf20Sopenharmony_ci return NULL; 17978c2ecf20Sopenharmony_ci 17988c2ecf20Sopenharmony_ci if (type == IOMMU_DOMAIN_DMA && 17998c2ecf20Sopenharmony_ci iommu_get_dma_cookie(&smmu_domain->domain)) { 18008c2ecf20Sopenharmony_ci kfree(smmu_domain); 18018c2ecf20Sopenharmony_ci return NULL; 18028c2ecf20Sopenharmony_ci } 18038c2ecf20Sopenharmony_ci 18048c2ecf20Sopenharmony_ci mutex_init(&smmu_domain->init_mutex); 18058c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&smmu_domain->devices); 18068c2ecf20Sopenharmony_ci spin_lock_init(&smmu_domain->devices_lock); 18078c2ecf20Sopenharmony_ci 18088c2ecf20Sopenharmony_ci return &smmu_domain->domain; 18098c2ecf20Sopenharmony_ci} 18108c2ecf20Sopenharmony_ci 18118c2ecf20Sopenharmony_cistatic int arm_smmu_bitmap_alloc(unsigned long *map, int span) 18128c2ecf20Sopenharmony_ci{ 18138c2ecf20Sopenharmony_ci int idx, size = 1 << span; 18148c2ecf20Sopenharmony_ci 18158c2ecf20Sopenharmony_ci do { 18168c2ecf20Sopenharmony_ci idx = find_first_zero_bit(map, size); 18178c2ecf20Sopenharmony_ci if (idx == size) 18188c2ecf20Sopenharmony_ci return -ENOSPC; 18198c2ecf20Sopenharmony_ci } while (test_and_set_bit(idx, map)); 18208c2ecf20Sopenharmony_ci 18218c2ecf20Sopenharmony_ci return idx; 18228c2ecf20Sopenharmony_ci} 18238c2ecf20Sopenharmony_ci 18248c2ecf20Sopenharmony_cistatic void arm_smmu_bitmap_free(unsigned long *map, int idx) 18258c2ecf20Sopenharmony_ci{ 18268c2ecf20Sopenharmony_ci clear_bit(idx, map); 18278c2ecf20Sopenharmony_ci} 18288c2ecf20Sopenharmony_ci 18298c2ecf20Sopenharmony_cistatic void arm_smmu_domain_free(struct iommu_domain *domain) 18308c2ecf20Sopenharmony_ci{ 18318c2ecf20Sopenharmony_ci struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); 18328c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu = smmu_domain->smmu; 18338c2ecf20Sopenharmony_ci 18348c2ecf20Sopenharmony_ci iommu_put_dma_cookie(domain); 18358c2ecf20Sopenharmony_ci free_io_pgtable_ops(smmu_domain->pgtbl_ops); 18368c2ecf20Sopenharmony_ci 18378c2ecf20Sopenharmony_ci /* Free the CD and ASID, if we allocated them */ 18388c2ecf20Sopenharmony_ci if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) { 18398c2ecf20Sopenharmony_ci struct arm_smmu_s1_cfg *cfg = &smmu_domain->s1_cfg; 18408c2ecf20Sopenharmony_ci 18418c2ecf20Sopenharmony_ci /* Prevent SVA from touching the CD while we're freeing it */ 18428c2ecf20Sopenharmony_ci mutex_lock(&arm_smmu_asid_lock); 18438c2ecf20Sopenharmony_ci if (cfg->cdcfg.cdtab) 18448c2ecf20Sopenharmony_ci arm_smmu_free_cd_tables(smmu_domain); 18458c2ecf20Sopenharmony_ci arm_smmu_free_asid(&cfg->cd); 18468c2ecf20Sopenharmony_ci mutex_unlock(&arm_smmu_asid_lock); 18478c2ecf20Sopenharmony_ci } else { 18488c2ecf20Sopenharmony_ci struct arm_smmu_s2_cfg *cfg = &smmu_domain->s2_cfg; 18498c2ecf20Sopenharmony_ci if (cfg->vmid) 18508c2ecf20Sopenharmony_ci arm_smmu_bitmap_free(smmu->vmid_map, cfg->vmid); 18518c2ecf20Sopenharmony_ci } 18528c2ecf20Sopenharmony_ci 18538c2ecf20Sopenharmony_ci kfree(smmu_domain); 18548c2ecf20Sopenharmony_ci} 18558c2ecf20Sopenharmony_ci 18568c2ecf20Sopenharmony_cistatic int arm_smmu_domain_finalise_s1(struct arm_smmu_domain *smmu_domain, 18578c2ecf20Sopenharmony_ci struct arm_smmu_master *master, 18588c2ecf20Sopenharmony_ci struct io_pgtable_cfg *pgtbl_cfg) 18598c2ecf20Sopenharmony_ci{ 18608c2ecf20Sopenharmony_ci int ret; 18618c2ecf20Sopenharmony_ci u32 asid; 18628c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu = smmu_domain->smmu; 18638c2ecf20Sopenharmony_ci struct arm_smmu_s1_cfg *cfg = &smmu_domain->s1_cfg; 18648c2ecf20Sopenharmony_ci typeof(&pgtbl_cfg->arm_lpae_s1_cfg.tcr) tcr = &pgtbl_cfg->arm_lpae_s1_cfg.tcr; 18658c2ecf20Sopenharmony_ci 18668c2ecf20Sopenharmony_ci refcount_set(&cfg->cd.refs, 1); 18678c2ecf20Sopenharmony_ci 18688c2ecf20Sopenharmony_ci /* Prevent SVA from modifying the ASID until it is written to the CD */ 18698c2ecf20Sopenharmony_ci mutex_lock(&arm_smmu_asid_lock); 18708c2ecf20Sopenharmony_ci ret = xa_alloc(&arm_smmu_asid_xa, &asid, &cfg->cd, 18718c2ecf20Sopenharmony_ci XA_LIMIT(1, (1 << smmu->asid_bits) - 1), GFP_KERNEL); 18728c2ecf20Sopenharmony_ci if (ret) 18738c2ecf20Sopenharmony_ci goto out_unlock; 18748c2ecf20Sopenharmony_ci 18758c2ecf20Sopenharmony_ci cfg->s1cdmax = master->ssid_bits; 18768c2ecf20Sopenharmony_ci 18778c2ecf20Sopenharmony_ci ret = arm_smmu_alloc_cd_tables(smmu_domain); 18788c2ecf20Sopenharmony_ci if (ret) 18798c2ecf20Sopenharmony_ci goto out_free_asid; 18808c2ecf20Sopenharmony_ci 18818c2ecf20Sopenharmony_ci cfg->cd.asid = (u16)asid; 18828c2ecf20Sopenharmony_ci cfg->cd.ttbr = pgtbl_cfg->arm_lpae_s1_cfg.ttbr; 18838c2ecf20Sopenharmony_ci cfg->cd.tcr = FIELD_PREP(CTXDESC_CD_0_TCR_T0SZ, tcr->tsz) | 18848c2ecf20Sopenharmony_ci FIELD_PREP(CTXDESC_CD_0_TCR_TG0, tcr->tg) | 18858c2ecf20Sopenharmony_ci FIELD_PREP(CTXDESC_CD_0_TCR_IRGN0, tcr->irgn) | 18868c2ecf20Sopenharmony_ci FIELD_PREP(CTXDESC_CD_0_TCR_ORGN0, tcr->orgn) | 18878c2ecf20Sopenharmony_ci FIELD_PREP(CTXDESC_CD_0_TCR_SH0, tcr->sh) | 18888c2ecf20Sopenharmony_ci FIELD_PREP(CTXDESC_CD_0_TCR_IPS, tcr->ips) | 18898c2ecf20Sopenharmony_ci CTXDESC_CD_0_TCR_EPD1 | CTXDESC_CD_0_AA64; 18908c2ecf20Sopenharmony_ci cfg->cd.mair = pgtbl_cfg->arm_lpae_s1_cfg.mair; 18918c2ecf20Sopenharmony_ci 18928c2ecf20Sopenharmony_ci /* 18938c2ecf20Sopenharmony_ci * Note that this will end up calling arm_smmu_sync_cd() before 18948c2ecf20Sopenharmony_ci * the master has been added to the devices list for this domain. 18958c2ecf20Sopenharmony_ci * This isn't an issue because the STE hasn't been installed yet. 18968c2ecf20Sopenharmony_ci */ 18978c2ecf20Sopenharmony_ci ret = arm_smmu_write_ctx_desc(smmu_domain, 0, &cfg->cd); 18988c2ecf20Sopenharmony_ci if (ret) 18998c2ecf20Sopenharmony_ci goto out_free_cd_tables; 19008c2ecf20Sopenharmony_ci 19018c2ecf20Sopenharmony_ci mutex_unlock(&arm_smmu_asid_lock); 19028c2ecf20Sopenharmony_ci return 0; 19038c2ecf20Sopenharmony_ci 19048c2ecf20Sopenharmony_ciout_free_cd_tables: 19058c2ecf20Sopenharmony_ci arm_smmu_free_cd_tables(smmu_domain); 19068c2ecf20Sopenharmony_ciout_free_asid: 19078c2ecf20Sopenharmony_ci arm_smmu_free_asid(&cfg->cd); 19088c2ecf20Sopenharmony_ciout_unlock: 19098c2ecf20Sopenharmony_ci mutex_unlock(&arm_smmu_asid_lock); 19108c2ecf20Sopenharmony_ci return ret; 19118c2ecf20Sopenharmony_ci} 19128c2ecf20Sopenharmony_ci 19138c2ecf20Sopenharmony_cistatic int arm_smmu_domain_finalise_s2(struct arm_smmu_domain *smmu_domain, 19148c2ecf20Sopenharmony_ci struct arm_smmu_master *master, 19158c2ecf20Sopenharmony_ci struct io_pgtable_cfg *pgtbl_cfg) 19168c2ecf20Sopenharmony_ci{ 19178c2ecf20Sopenharmony_ci int vmid; 19188c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu = smmu_domain->smmu; 19198c2ecf20Sopenharmony_ci struct arm_smmu_s2_cfg *cfg = &smmu_domain->s2_cfg; 19208c2ecf20Sopenharmony_ci typeof(&pgtbl_cfg->arm_lpae_s2_cfg.vtcr) vtcr; 19218c2ecf20Sopenharmony_ci 19228c2ecf20Sopenharmony_ci vmid = arm_smmu_bitmap_alloc(smmu->vmid_map, smmu->vmid_bits); 19238c2ecf20Sopenharmony_ci if (vmid < 0) 19248c2ecf20Sopenharmony_ci return vmid; 19258c2ecf20Sopenharmony_ci 19268c2ecf20Sopenharmony_ci vtcr = &pgtbl_cfg->arm_lpae_s2_cfg.vtcr; 19278c2ecf20Sopenharmony_ci cfg->vmid = (u16)vmid; 19288c2ecf20Sopenharmony_ci cfg->vttbr = pgtbl_cfg->arm_lpae_s2_cfg.vttbr; 19298c2ecf20Sopenharmony_ci cfg->vtcr = FIELD_PREP(STRTAB_STE_2_VTCR_S2T0SZ, vtcr->tsz) | 19308c2ecf20Sopenharmony_ci FIELD_PREP(STRTAB_STE_2_VTCR_S2SL0, vtcr->sl) | 19318c2ecf20Sopenharmony_ci FIELD_PREP(STRTAB_STE_2_VTCR_S2IR0, vtcr->irgn) | 19328c2ecf20Sopenharmony_ci FIELD_PREP(STRTAB_STE_2_VTCR_S2OR0, vtcr->orgn) | 19338c2ecf20Sopenharmony_ci FIELD_PREP(STRTAB_STE_2_VTCR_S2SH0, vtcr->sh) | 19348c2ecf20Sopenharmony_ci FIELD_PREP(STRTAB_STE_2_VTCR_S2TG, vtcr->tg) | 19358c2ecf20Sopenharmony_ci FIELD_PREP(STRTAB_STE_2_VTCR_S2PS, vtcr->ps); 19368c2ecf20Sopenharmony_ci return 0; 19378c2ecf20Sopenharmony_ci} 19388c2ecf20Sopenharmony_ci 19398c2ecf20Sopenharmony_cistatic int arm_smmu_domain_finalise(struct iommu_domain *domain, 19408c2ecf20Sopenharmony_ci struct arm_smmu_master *master) 19418c2ecf20Sopenharmony_ci{ 19428c2ecf20Sopenharmony_ci int ret; 19438c2ecf20Sopenharmony_ci unsigned long ias, oas; 19448c2ecf20Sopenharmony_ci enum io_pgtable_fmt fmt; 19458c2ecf20Sopenharmony_ci struct io_pgtable_cfg pgtbl_cfg; 19468c2ecf20Sopenharmony_ci struct io_pgtable_ops *pgtbl_ops; 19478c2ecf20Sopenharmony_ci int (*finalise_stage_fn)(struct arm_smmu_domain *, 19488c2ecf20Sopenharmony_ci struct arm_smmu_master *, 19498c2ecf20Sopenharmony_ci struct io_pgtable_cfg *); 19508c2ecf20Sopenharmony_ci struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); 19518c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu = smmu_domain->smmu; 19528c2ecf20Sopenharmony_ci 19538c2ecf20Sopenharmony_ci if (domain->type == IOMMU_DOMAIN_IDENTITY) { 19548c2ecf20Sopenharmony_ci smmu_domain->stage = ARM_SMMU_DOMAIN_BYPASS; 19558c2ecf20Sopenharmony_ci return 0; 19568c2ecf20Sopenharmony_ci } 19578c2ecf20Sopenharmony_ci 19588c2ecf20Sopenharmony_ci /* Restrict the stage to what we can actually support */ 19598c2ecf20Sopenharmony_ci if (!(smmu->features & ARM_SMMU_FEAT_TRANS_S1)) 19608c2ecf20Sopenharmony_ci smmu_domain->stage = ARM_SMMU_DOMAIN_S2; 19618c2ecf20Sopenharmony_ci if (!(smmu->features & ARM_SMMU_FEAT_TRANS_S2)) 19628c2ecf20Sopenharmony_ci smmu_domain->stage = ARM_SMMU_DOMAIN_S1; 19638c2ecf20Sopenharmony_ci 19648c2ecf20Sopenharmony_ci switch (smmu_domain->stage) { 19658c2ecf20Sopenharmony_ci case ARM_SMMU_DOMAIN_S1: 19668c2ecf20Sopenharmony_ci ias = (smmu->features & ARM_SMMU_FEAT_VAX) ? 52 : 48; 19678c2ecf20Sopenharmony_ci ias = min_t(unsigned long, ias, VA_BITS); 19688c2ecf20Sopenharmony_ci oas = smmu->ias; 19698c2ecf20Sopenharmony_ci fmt = ARM_64_LPAE_S1; 19708c2ecf20Sopenharmony_ci finalise_stage_fn = arm_smmu_domain_finalise_s1; 19718c2ecf20Sopenharmony_ci break; 19728c2ecf20Sopenharmony_ci case ARM_SMMU_DOMAIN_NESTED: 19738c2ecf20Sopenharmony_ci case ARM_SMMU_DOMAIN_S2: 19748c2ecf20Sopenharmony_ci ias = smmu->ias; 19758c2ecf20Sopenharmony_ci oas = smmu->oas; 19768c2ecf20Sopenharmony_ci fmt = ARM_64_LPAE_S2; 19778c2ecf20Sopenharmony_ci finalise_stage_fn = arm_smmu_domain_finalise_s2; 19788c2ecf20Sopenharmony_ci break; 19798c2ecf20Sopenharmony_ci default: 19808c2ecf20Sopenharmony_ci return -EINVAL; 19818c2ecf20Sopenharmony_ci } 19828c2ecf20Sopenharmony_ci 19838c2ecf20Sopenharmony_ci pgtbl_cfg = (struct io_pgtable_cfg) { 19848c2ecf20Sopenharmony_ci .pgsize_bitmap = smmu->pgsize_bitmap, 19858c2ecf20Sopenharmony_ci .ias = ias, 19868c2ecf20Sopenharmony_ci .oas = oas, 19878c2ecf20Sopenharmony_ci .coherent_walk = smmu->features & ARM_SMMU_FEAT_COHERENCY, 19888c2ecf20Sopenharmony_ci .tlb = &arm_smmu_flush_ops, 19898c2ecf20Sopenharmony_ci .iommu_dev = smmu->dev, 19908c2ecf20Sopenharmony_ci }; 19918c2ecf20Sopenharmony_ci 19928c2ecf20Sopenharmony_ci if (smmu_domain->non_strict) 19938c2ecf20Sopenharmony_ci pgtbl_cfg.quirks |= IO_PGTABLE_QUIRK_NON_STRICT; 19948c2ecf20Sopenharmony_ci 19958c2ecf20Sopenharmony_ci pgtbl_ops = alloc_io_pgtable_ops(fmt, &pgtbl_cfg, smmu_domain); 19968c2ecf20Sopenharmony_ci if (!pgtbl_ops) 19978c2ecf20Sopenharmony_ci return -ENOMEM; 19988c2ecf20Sopenharmony_ci 19998c2ecf20Sopenharmony_ci domain->pgsize_bitmap = pgtbl_cfg.pgsize_bitmap; 20008c2ecf20Sopenharmony_ci domain->geometry.aperture_end = (1UL << pgtbl_cfg.ias) - 1; 20018c2ecf20Sopenharmony_ci domain->geometry.force_aperture = true; 20028c2ecf20Sopenharmony_ci 20038c2ecf20Sopenharmony_ci ret = finalise_stage_fn(smmu_domain, master, &pgtbl_cfg); 20048c2ecf20Sopenharmony_ci if (ret < 0) { 20058c2ecf20Sopenharmony_ci free_io_pgtable_ops(pgtbl_ops); 20068c2ecf20Sopenharmony_ci return ret; 20078c2ecf20Sopenharmony_ci } 20088c2ecf20Sopenharmony_ci 20098c2ecf20Sopenharmony_ci smmu_domain->pgtbl_ops = pgtbl_ops; 20108c2ecf20Sopenharmony_ci return 0; 20118c2ecf20Sopenharmony_ci} 20128c2ecf20Sopenharmony_ci 20138c2ecf20Sopenharmony_cistatic __le64 *arm_smmu_get_step_for_sid(struct arm_smmu_device *smmu, u32 sid) 20148c2ecf20Sopenharmony_ci{ 20158c2ecf20Sopenharmony_ci __le64 *step; 20168c2ecf20Sopenharmony_ci struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg; 20178c2ecf20Sopenharmony_ci 20188c2ecf20Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB) { 20198c2ecf20Sopenharmony_ci struct arm_smmu_strtab_l1_desc *l1_desc; 20208c2ecf20Sopenharmony_ci int idx; 20218c2ecf20Sopenharmony_ci 20228c2ecf20Sopenharmony_ci /* Two-level walk */ 20238c2ecf20Sopenharmony_ci idx = (sid >> STRTAB_SPLIT) * STRTAB_L1_DESC_DWORDS; 20248c2ecf20Sopenharmony_ci l1_desc = &cfg->l1_desc[idx]; 20258c2ecf20Sopenharmony_ci idx = (sid & ((1 << STRTAB_SPLIT) - 1)) * STRTAB_STE_DWORDS; 20268c2ecf20Sopenharmony_ci step = &l1_desc->l2ptr[idx]; 20278c2ecf20Sopenharmony_ci } else { 20288c2ecf20Sopenharmony_ci /* Simple linear lookup */ 20298c2ecf20Sopenharmony_ci step = &cfg->strtab[sid * STRTAB_STE_DWORDS]; 20308c2ecf20Sopenharmony_ci } 20318c2ecf20Sopenharmony_ci 20328c2ecf20Sopenharmony_ci return step; 20338c2ecf20Sopenharmony_ci} 20348c2ecf20Sopenharmony_ci 20358c2ecf20Sopenharmony_cistatic void arm_smmu_install_ste_for_dev(struct arm_smmu_master *master) 20368c2ecf20Sopenharmony_ci{ 20378c2ecf20Sopenharmony_ci int i, j; 20388c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu = master->smmu; 20398c2ecf20Sopenharmony_ci 20408c2ecf20Sopenharmony_ci for (i = 0; i < master->num_sids; ++i) { 20418c2ecf20Sopenharmony_ci u32 sid = master->sids[i]; 20428c2ecf20Sopenharmony_ci __le64 *step = arm_smmu_get_step_for_sid(smmu, sid); 20438c2ecf20Sopenharmony_ci 20448c2ecf20Sopenharmony_ci /* Bridged PCI devices may end up with duplicated IDs */ 20458c2ecf20Sopenharmony_ci for (j = 0; j < i; j++) 20468c2ecf20Sopenharmony_ci if (master->sids[j] == sid) 20478c2ecf20Sopenharmony_ci break; 20488c2ecf20Sopenharmony_ci if (j < i) 20498c2ecf20Sopenharmony_ci continue; 20508c2ecf20Sopenharmony_ci 20518c2ecf20Sopenharmony_ci arm_smmu_write_strtab_ent(master, sid, step); 20528c2ecf20Sopenharmony_ci } 20538c2ecf20Sopenharmony_ci} 20548c2ecf20Sopenharmony_ci 20558c2ecf20Sopenharmony_cistatic bool arm_smmu_ats_supported(struct arm_smmu_master *master) 20568c2ecf20Sopenharmony_ci{ 20578c2ecf20Sopenharmony_ci struct device *dev = master->dev; 20588c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu = master->smmu; 20598c2ecf20Sopenharmony_ci struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); 20608c2ecf20Sopenharmony_ci 20618c2ecf20Sopenharmony_ci if (!(smmu->features & ARM_SMMU_FEAT_ATS)) 20628c2ecf20Sopenharmony_ci return false; 20638c2ecf20Sopenharmony_ci 20648c2ecf20Sopenharmony_ci if (!(fwspec->flags & IOMMU_FWSPEC_PCI_RC_ATS)) 20658c2ecf20Sopenharmony_ci return false; 20668c2ecf20Sopenharmony_ci 20678c2ecf20Sopenharmony_ci return dev_is_pci(dev) && pci_ats_supported(to_pci_dev(dev)); 20688c2ecf20Sopenharmony_ci} 20698c2ecf20Sopenharmony_ci 20708c2ecf20Sopenharmony_cistatic void arm_smmu_enable_ats(struct arm_smmu_master *master) 20718c2ecf20Sopenharmony_ci{ 20728c2ecf20Sopenharmony_ci size_t stu; 20738c2ecf20Sopenharmony_ci struct pci_dev *pdev; 20748c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu = master->smmu; 20758c2ecf20Sopenharmony_ci struct arm_smmu_domain *smmu_domain = master->domain; 20768c2ecf20Sopenharmony_ci 20778c2ecf20Sopenharmony_ci /* Don't enable ATS at the endpoint if it's not enabled in the STE */ 20788c2ecf20Sopenharmony_ci if (!master->ats_enabled) 20798c2ecf20Sopenharmony_ci return; 20808c2ecf20Sopenharmony_ci 20818c2ecf20Sopenharmony_ci /* Smallest Translation Unit: log2 of the smallest supported granule */ 20828c2ecf20Sopenharmony_ci stu = __ffs(smmu->pgsize_bitmap); 20838c2ecf20Sopenharmony_ci pdev = to_pci_dev(master->dev); 20848c2ecf20Sopenharmony_ci 20858c2ecf20Sopenharmony_ci atomic_inc(&smmu_domain->nr_ats_masters); 20868c2ecf20Sopenharmony_ci arm_smmu_atc_inv_domain(smmu_domain, 0, 0, 0); 20878c2ecf20Sopenharmony_ci if (pci_enable_ats(pdev, stu)) 20888c2ecf20Sopenharmony_ci dev_err(master->dev, "Failed to enable ATS (STU %zu)\n", stu); 20898c2ecf20Sopenharmony_ci} 20908c2ecf20Sopenharmony_ci 20918c2ecf20Sopenharmony_cistatic void arm_smmu_disable_ats(struct arm_smmu_master *master) 20928c2ecf20Sopenharmony_ci{ 20938c2ecf20Sopenharmony_ci struct arm_smmu_domain *smmu_domain = master->domain; 20948c2ecf20Sopenharmony_ci 20958c2ecf20Sopenharmony_ci if (!master->ats_enabled) 20968c2ecf20Sopenharmony_ci return; 20978c2ecf20Sopenharmony_ci 20988c2ecf20Sopenharmony_ci pci_disable_ats(to_pci_dev(master->dev)); 20998c2ecf20Sopenharmony_ci /* 21008c2ecf20Sopenharmony_ci * Ensure ATS is disabled at the endpoint before we issue the 21018c2ecf20Sopenharmony_ci * ATC invalidation via the SMMU. 21028c2ecf20Sopenharmony_ci */ 21038c2ecf20Sopenharmony_ci wmb(); 21048c2ecf20Sopenharmony_ci arm_smmu_atc_inv_master(master); 21058c2ecf20Sopenharmony_ci atomic_dec(&smmu_domain->nr_ats_masters); 21068c2ecf20Sopenharmony_ci} 21078c2ecf20Sopenharmony_ci 21088c2ecf20Sopenharmony_cistatic int arm_smmu_enable_pasid(struct arm_smmu_master *master) 21098c2ecf20Sopenharmony_ci{ 21108c2ecf20Sopenharmony_ci int ret; 21118c2ecf20Sopenharmony_ci int features; 21128c2ecf20Sopenharmony_ci int num_pasids; 21138c2ecf20Sopenharmony_ci struct pci_dev *pdev; 21148c2ecf20Sopenharmony_ci 21158c2ecf20Sopenharmony_ci if (!dev_is_pci(master->dev)) 21168c2ecf20Sopenharmony_ci return -ENODEV; 21178c2ecf20Sopenharmony_ci 21188c2ecf20Sopenharmony_ci pdev = to_pci_dev(master->dev); 21198c2ecf20Sopenharmony_ci 21208c2ecf20Sopenharmony_ci features = pci_pasid_features(pdev); 21218c2ecf20Sopenharmony_ci if (features < 0) 21228c2ecf20Sopenharmony_ci return features; 21238c2ecf20Sopenharmony_ci 21248c2ecf20Sopenharmony_ci num_pasids = pci_max_pasids(pdev); 21258c2ecf20Sopenharmony_ci if (num_pasids <= 0) 21268c2ecf20Sopenharmony_ci return num_pasids; 21278c2ecf20Sopenharmony_ci 21288c2ecf20Sopenharmony_ci ret = pci_enable_pasid(pdev, features); 21298c2ecf20Sopenharmony_ci if (ret) { 21308c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to enable PASID\n"); 21318c2ecf20Sopenharmony_ci return ret; 21328c2ecf20Sopenharmony_ci } 21338c2ecf20Sopenharmony_ci 21348c2ecf20Sopenharmony_ci master->ssid_bits = min_t(u8, ilog2(num_pasids), 21358c2ecf20Sopenharmony_ci master->smmu->ssid_bits); 21368c2ecf20Sopenharmony_ci return 0; 21378c2ecf20Sopenharmony_ci} 21388c2ecf20Sopenharmony_ci 21398c2ecf20Sopenharmony_cistatic void arm_smmu_disable_pasid(struct arm_smmu_master *master) 21408c2ecf20Sopenharmony_ci{ 21418c2ecf20Sopenharmony_ci struct pci_dev *pdev; 21428c2ecf20Sopenharmony_ci 21438c2ecf20Sopenharmony_ci if (!dev_is_pci(master->dev)) 21448c2ecf20Sopenharmony_ci return; 21458c2ecf20Sopenharmony_ci 21468c2ecf20Sopenharmony_ci pdev = to_pci_dev(master->dev); 21478c2ecf20Sopenharmony_ci 21488c2ecf20Sopenharmony_ci if (!pdev->pasid_enabled) 21498c2ecf20Sopenharmony_ci return; 21508c2ecf20Sopenharmony_ci 21518c2ecf20Sopenharmony_ci master->ssid_bits = 0; 21528c2ecf20Sopenharmony_ci pci_disable_pasid(pdev); 21538c2ecf20Sopenharmony_ci} 21548c2ecf20Sopenharmony_ci 21558c2ecf20Sopenharmony_cistatic void arm_smmu_detach_dev(struct arm_smmu_master *master) 21568c2ecf20Sopenharmony_ci{ 21578c2ecf20Sopenharmony_ci unsigned long flags; 21588c2ecf20Sopenharmony_ci struct arm_smmu_domain *smmu_domain = master->domain; 21598c2ecf20Sopenharmony_ci 21608c2ecf20Sopenharmony_ci if (!smmu_domain) 21618c2ecf20Sopenharmony_ci return; 21628c2ecf20Sopenharmony_ci 21638c2ecf20Sopenharmony_ci arm_smmu_disable_ats(master); 21648c2ecf20Sopenharmony_ci 21658c2ecf20Sopenharmony_ci spin_lock_irqsave(&smmu_domain->devices_lock, flags); 21668c2ecf20Sopenharmony_ci list_del(&master->domain_head); 21678c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&smmu_domain->devices_lock, flags); 21688c2ecf20Sopenharmony_ci 21698c2ecf20Sopenharmony_ci master->domain = NULL; 21708c2ecf20Sopenharmony_ci master->ats_enabled = false; 21718c2ecf20Sopenharmony_ci arm_smmu_install_ste_for_dev(master); 21728c2ecf20Sopenharmony_ci} 21738c2ecf20Sopenharmony_ci 21748c2ecf20Sopenharmony_cistatic int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) 21758c2ecf20Sopenharmony_ci{ 21768c2ecf20Sopenharmony_ci int ret = 0; 21778c2ecf20Sopenharmony_ci unsigned long flags; 21788c2ecf20Sopenharmony_ci struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); 21798c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu; 21808c2ecf20Sopenharmony_ci struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); 21818c2ecf20Sopenharmony_ci struct arm_smmu_master *master; 21828c2ecf20Sopenharmony_ci 21838c2ecf20Sopenharmony_ci if (!fwspec) 21848c2ecf20Sopenharmony_ci return -ENOENT; 21858c2ecf20Sopenharmony_ci 21868c2ecf20Sopenharmony_ci master = dev_iommu_priv_get(dev); 21878c2ecf20Sopenharmony_ci smmu = master->smmu; 21888c2ecf20Sopenharmony_ci 21898c2ecf20Sopenharmony_ci /* 21908c2ecf20Sopenharmony_ci * Checking that SVA is disabled ensures that this device isn't bound to 21918c2ecf20Sopenharmony_ci * any mm, and can be safely detached from its old domain. Bonds cannot 21928c2ecf20Sopenharmony_ci * be removed concurrently since we're holding the group mutex. 21938c2ecf20Sopenharmony_ci */ 21948c2ecf20Sopenharmony_ci if (arm_smmu_master_sva_enabled(master)) { 21958c2ecf20Sopenharmony_ci dev_err(dev, "cannot attach - SVA enabled\n"); 21968c2ecf20Sopenharmony_ci return -EBUSY; 21978c2ecf20Sopenharmony_ci } 21988c2ecf20Sopenharmony_ci 21998c2ecf20Sopenharmony_ci arm_smmu_detach_dev(master); 22008c2ecf20Sopenharmony_ci 22018c2ecf20Sopenharmony_ci mutex_lock(&smmu_domain->init_mutex); 22028c2ecf20Sopenharmony_ci 22038c2ecf20Sopenharmony_ci if (!smmu_domain->smmu) { 22048c2ecf20Sopenharmony_ci smmu_domain->smmu = smmu; 22058c2ecf20Sopenharmony_ci ret = arm_smmu_domain_finalise(domain, master); 22068c2ecf20Sopenharmony_ci if (ret) { 22078c2ecf20Sopenharmony_ci smmu_domain->smmu = NULL; 22088c2ecf20Sopenharmony_ci goto out_unlock; 22098c2ecf20Sopenharmony_ci } 22108c2ecf20Sopenharmony_ci } else if (smmu_domain->smmu != smmu) { 22118c2ecf20Sopenharmony_ci dev_err(dev, 22128c2ecf20Sopenharmony_ci "cannot attach to SMMU %s (upstream of %s)\n", 22138c2ecf20Sopenharmony_ci dev_name(smmu_domain->smmu->dev), 22148c2ecf20Sopenharmony_ci dev_name(smmu->dev)); 22158c2ecf20Sopenharmony_ci ret = -ENXIO; 22168c2ecf20Sopenharmony_ci goto out_unlock; 22178c2ecf20Sopenharmony_ci } else if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1 && 22188c2ecf20Sopenharmony_ci master->ssid_bits != smmu_domain->s1_cfg.s1cdmax) { 22198c2ecf20Sopenharmony_ci dev_err(dev, 22208c2ecf20Sopenharmony_ci "cannot attach to incompatible domain (%u SSID bits != %u)\n", 22218c2ecf20Sopenharmony_ci smmu_domain->s1_cfg.s1cdmax, master->ssid_bits); 22228c2ecf20Sopenharmony_ci ret = -EINVAL; 22238c2ecf20Sopenharmony_ci goto out_unlock; 22248c2ecf20Sopenharmony_ci } 22258c2ecf20Sopenharmony_ci 22268c2ecf20Sopenharmony_ci master->domain = smmu_domain; 22278c2ecf20Sopenharmony_ci 22288c2ecf20Sopenharmony_ci if (smmu_domain->stage != ARM_SMMU_DOMAIN_BYPASS) 22298c2ecf20Sopenharmony_ci master->ats_enabled = arm_smmu_ats_supported(master); 22308c2ecf20Sopenharmony_ci 22318c2ecf20Sopenharmony_ci arm_smmu_install_ste_for_dev(master); 22328c2ecf20Sopenharmony_ci 22338c2ecf20Sopenharmony_ci spin_lock_irqsave(&smmu_domain->devices_lock, flags); 22348c2ecf20Sopenharmony_ci list_add(&master->domain_head, &smmu_domain->devices); 22358c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&smmu_domain->devices_lock, flags); 22368c2ecf20Sopenharmony_ci 22378c2ecf20Sopenharmony_ci arm_smmu_enable_ats(master); 22388c2ecf20Sopenharmony_ci 22398c2ecf20Sopenharmony_ciout_unlock: 22408c2ecf20Sopenharmony_ci mutex_unlock(&smmu_domain->init_mutex); 22418c2ecf20Sopenharmony_ci return ret; 22428c2ecf20Sopenharmony_ci} 22438c2ecf20Sopenharmony_ci 22448c2ecf20Sopenharmony_cistatic int arm_smmu_map(struct iommu_domain *domain, unsigned long iova, 22458c2ecf20Sopenharmony_ci phys_addr_t paddr, size_t size, int prot, gfp_t gfp) 22468c2ecf20Sopenharmony_ci{ 22478c2ecf20Sopenharmony_ci struct io_pgtable_ops *ops = to_smmu_domain(domain)->pgtbl_ops; 22488c2ecf20Sopenharmony_ci 22498c2ecf20Sopenharmony_ci if (!ops) 22508c2ecf20Sopenharmony_ci return -ENODEV; 22518c2ecf20Sopenharmony_ci 22528c2ecf20Sopenharmony_ci return ops->map(ops, iova, paddr, size, prot, gfp); 22538c2ecf20Sopenharmony_ci} 22548c2ecf20Sopenharmony_ci 22558c2ecf20Sopenharmony_cistatic size_t arm_smmu_unmap(struct iommu_domain *domain, unsigned long iova, 22568c2ecf20Sopenharmony_ci size_t size, struct iommu_iotlb_gather *gather) 22578c2ecf20Sopenharmony_ci{ 22588c2ecf20Sopenharmony_ci struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); 22598c2ecf20Sopenharmony_ci struct io_pgtable_ops *ops = smmu_domain->pgtbl_ops; 22608c2ecf20Sopenharmony_ci 22618c2ecf20Sopenharmony_ci if (!ops) 22628c2ecf20Sopenharmony_ci return 0; 22638c2ecf20Sopenharmony_ci 22648c2ecf20Sopenharmony_ci return ops->unmap(ops, iova, size, gather); 22658c2ecf20Sopenharmony_ci} 22668c2ecf20Sopenharmony_ci 22678c2ecf20Sopenharmony_cistatic void arm_smmu_flush_iotlb_all(struct iommu_domain *domain) 22688c2ecf20Sopenharmony_ci{ 22698c2ecf20Sopenharmony_ci struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); 22708c2ecf20Sopenharmony_ci 22718c2ecf20Sopenharmony_ci if (smmu_domain->smmu) 22728c2ecf20Sopenharmony_ci arm_smmu_tlb_inv_context(smmu_domain); 22738c2ecf20Sopenharmony_ci} 22748c2ecf20Sopenharmony_ci 22758c2ecf20Sopenharmony_cistatic void arm_smmu_iotlb_sync(struct iommu_domain *domain, 22768c2ecf20Sopenharmony_ci struct iommu_iotlb_gather *gather) 22778c2ecf20Sopenharmony_ci{ 22788c2ecf20Sopenharmony_ci struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); 22798c2ecf20Sopenharmony_ci 22808c2ecf20Sopenharmony_ci arm_smmu_tlb_inv_range(gather->start, gather->end - gather->start + 1, 22818c2ecf20Sopenharmony_ci gather->pgsize, true, smmu_domain); 22828c2ecf20Sopenharmony_ci} 22838c2ecf20Sopenharmony_ci 22848c2ecf20Sopenharmony_cistatic phys_addr_t 22858c2ecf20Sopenharmony_ciarm_smmu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova) 22868c2ecf20Sopenharmony_ci{ 22878c2ecf20Sopenharmony_ci struct io_pgtable_ops *ops = to_smmu_domain(domain)->pgtbl_ops; 22888c2ecf20Sopenharmony_ci 22898c2ecf20Sopenharmony_ci if (domain->type == IOMMU_DOMAIN_IDENTITY) 22908c2ecf20Sopenharmony_ci return iova; 22918c2ecf20Sopenharmony_ci 22928c2ecf20Sopenharmony_ci if (!ops) 22938c2ecf20Sopenharmony_ci return 0; 22948c2ecf20Sopenharmony_ci 22958c2ecf20Sopenharmony_ci return ops->iova_to_phys(ops, iova); 22968c2ecf20Sopenharmony_ci} 22978c2ecf20Sopenharmony_ci 22988c2ecf20Sopenharmony_cistatic struct platform_driver arm_smmu_driver; 22998c2ecf20Sopenharmony_ci 23008c2ecf20Sopenharmony_cistatic 23018c2ecf20Sopenharmony_cistruct arm_smmu_device *arm_smmu_get_by_fwnode(struct fwnode_handle *fwnode) 23028c2ecf20Sopenharmony_ci{ 23038c2ecf20Sopenharmony_ci struct device *dev = driver_find_device_by_fwnode(&arm_smmu_driver.driver, 23048c2ecf20Sopenharmony_ci fwnode); 23058c2ecf20Sopenharmony_ci put_device(dev); 23068c2ecf20Sopenharmony_ci return dev ? dev_get_drvdata(dev) : NULL; 23078c2ecf20Sopenharmony_ci} 23088c2ecf20Sopenharmony_ci 23098c2ecf20Sopenharmony_cistatic bool arm_smmu_sid_in_range(struct arm_smmu_device *smmu, u32 sid) 23108c2ecf20Sopenharmony_ci{ 23118c2ecf20Sopenharmony_ci unsigned long limit = smmu->strtab_cfg.num_l1_ents; 23128c2ecf20Sopenharmony_ci 23138c2ecf20Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB) 23148c2ecf20Sopenharmony_ci limit *= 1UL << STRTAB_SPLIT; 23158c2ecf20Sopenharmony_ci 23168c2ecf20Sopenharmony_ci return sid < limit; 23178c2ecf20Sopenharmony_ci} 23188c2ecf20Sopenharmony_ci 23198c2ecf20Sopenharmony_cistatic struct iommu_ops arm_smmu_ops; 23208c2ecf20Sopenharmony_ci 23218c2ecf20Sopenharmony_cistatic struct iommu_device *arm_smmu_probe_device(struct device *dev) 23228c2ecf20Sopenharmony_ci{ 23238c2ecf20Sopenharmony_ci int i, ret; 23248c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu; 23258c2ecf20Sopenharmony_ci struct arm_smmu_master *master; 23268c2ecf20Sopenharmony_ci struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); 23278c2ecf20Sopenharmony_ci 23288c2ecf20Sopenharmony_ci if (!fwspec || fwspec->ops != &arm_smmu_ops) 23298c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 23308c2ecf20Sopenharmony_ci 23318c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(dev_iommu_priv_get(dev))) 23328c2ecf20Sopenharmony_ci return ERR_PTR(-EBUSY); 23338c2ecf20Sopenharmony_ci 23348c2ecf20Sopenharmony_ci smmu = arm_smmu_get_by_fwnode(fwspec->iommu_fwnode); 23358c2ecf20Sopenharmony_ci if (!smmu) 23368c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 23378c2ecf20Sopenharmony_ci 23388c2ecf20Sopenharmony_ci master = kzalloc(sizeof(*master), GFP_KERNEL); 23398c2ecf20Sopenharmony_ci if (!master) 23408c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 23418c2ecf20Sopenharmony_ci 23428c2ecf20Sopenharmony_ci master->dev = dev; 23438c2ecf20Sopenharmony_ci master->smmu = smmu; 23448c2ecf20Sopenharmony_ci master->sids = fwspec->ids; 23458c2ecf20Sopenharmony_ci master->num_sids = fwspec->num_ids; 23468c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&master->bonds); 23478c2ecf20Sopenharmony_ci dev_iommu_priv_set(dev, master); 23488c2ecf20Sopenharmony_ci 23498c2ecf20Sopenharmony_ci /* Check the SIDs are in range of the SMMU and our stream table */ 23508c2ecf20Sopenharmony_ci for (i = 0; i < master->num_sids; i++) { 23518c2ecf20Sopenharmony_ci u32 sid = master->sids[i]; 23528c2ecf20Sopenharmony_ci 23538c2ecf20Sopenharmony_ci if (!arm_smmu_sid_in_range(smmu, sid)) { 23548c2ecf20Sopenharmony_ci ret = -ERANGE; 23558c2ecf20Sopenharmony_ci goto err_free_master; 23568c2ecf20Sopenharmony_ci } 23578c2ecf20Sopenharmony_ci 23588c2ecf20Sopenharmony_ci /* Ensure l2 strtab is initialised */ 23598c2ecf20Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB) { 23608c2ecf20Sopenharmony_ci ret = arm_smmu_init_l2_strtab(smmu, sid); 23618c2ecf20Sopenharmony_ci if (ret) 23628c2ecf20Sopenharmony_ci goto err_free_master; 23638c2ecf20Sopenharmony_ci } 23648c2ecf20Sopenharmony_ci } 23658c2ecf20Sopenharmony_ci 23668c2ecf20Sopenharmony_ci master->ssid_bits = min(smmu->ssid_bits, fwspec->num_pasid_bits); 23678c2ecf20Sopenharmony_ci 23688c2ecf20Sopenharmony_ci /* 23698c2ecf20Sopenharmony_ci * Note that PASID must be enabled before, and disabled after ATS: 23708c2ecf20Sopenharmony_ci * PCI Express Base 4.0r1.0 - 10.5.1.3 ATS Control Register 23718c2ecf20Sopenharmony_ci * 23728c2ecf20Sopenharmony_ci * Behavior is undefined if this bit is Set and the value of the PASID 23738c2ecf20Sopenharmony_ci * Enable, Execute Requested Enable, or Privileged Mode Requested bits 23748c2ecf20Sopenharmony_ci * are changed. 23758c2ecf20Sopenharmony_ci */ 23768c2ecf20Sopenharmony_ci arm_smmu_enable_pasid(master); 23778c2ecf20Sopenharmony_ci 23788c2ecf20Sopenharmony_ci if (!(smmu->features & ARM_SMMU_FEAT_2_LVL_CDTAB)) 23798c2ecf20Sopenharmony_ci master->ssid_bits = min_t(u8, master->ssid_bits, 23808c2ecf20Sopenharmony_ci CTXDESC_LINEAR_CDMAX); 23818c2ecf20Sopenharmony_ci 23828c2ecf20Sopenharmony_ci return &smmu->iommu; 23838c2ecf20Sopenharmony_ci 23848c2ecf20Sopenharmony_cierr_free_master: 23858c2ecf20Sopenharmony_ci kfree(master); 23868c2ecf20Sopenharmony_ci dev_iommu_priv_set(dev, NULL); 23878c2ecf20Sopenharmony_ci return ERR_PTR(ret); 23888c2ecf20Sopenharmony_ci} 23898c2ecf20Sopenharmony_ci 23908c2ecf20Sopenharmony_cistatic void arm_smmu_release_device(struct device *dev) 23918c2ecf20Sopenharmony_ci{ 23928c2ecf20Sopenharmony_ci struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); 23938c2ecf20Sopenharmony_ci struct arm_smmu_master *master; 23948c2ecf20Sopenharmony_ci 23958c2ecf20Sopenharmony_ci if (!fwspec || fwspec->ops != &arm_smmu_ops) 23968c2ecf20Sopenharmony_ci return; 23978c2ecf20Sopenharmony_ci 23988c2ecf20Sopenharmony_ci master = dev_iommu_priv_get(dev); 23998c2ecf20Sopenharmony_ci WARN_ON(arm_smmu_master_sva_enabled(master)); 24008c2ecf20Sopenharmony_ci arm_smmu_detach_dev(master); 24018c2ecf20Sopenharmony_ci arm_smmu_disable_pasid(master); 24028c2ecf20Sopenharmony_ci kfree(master); 24038c2ecf20Sopenharmony_ci iommu_fwspec_free(dev); 24048c2ecf20Sopenharmony_ci} 24058c2ecf20Sopenharmony_ci 24068c2ecf20Sopenharmony_cistatic struct iommu_group *arm_smmu_device_group(struct device *dev) 24078c2ecf20Sopenharmony_ci{ 24088c2ecf20Sopenharmony_ci struct iommu_group *group; 24098c2ecf20Sopenharmony_ci 24108c2ecf20Sopenharmony_ci /* 24118c2ecf20Sopenharmony_ci * We don't support devices sharing stream IDs other than PCI RID 24128c2ecf20Sopenharmony_ci * aliases, since the necessary ID-to-device lookup becomes rather 24138c2ecf20Sopenharmony_ci * impractical given a potential sparse 32-bit stream ID space. 24148c2ecf20Sopenharmony_ci */ 24158c2ecf20Sopenharmony_ci if (dev_is_pci(dev)) 24168c2ecf20Sopenharmony_ci group = pci_device_group(dev); 24178c2ecf20Sopenharmony_ci else 24188c2ecf20Sopenharmony_ci group = generic_device_group(dev); 24198c2ecf20Sopenharmony_ci 24208c2ecf20Sopenharmony_ci return group; 24218c2ecf20Sopenharmony_ci} 24228c2ecf20Sopenharmony_ci 24238c2ecf20Sopenharmony_cistatic int arm_smmu_domain_get_attr(struct iommu_domain *domain, 24248c2ecf20Sopenharmony_ci enum iommu_attr attr, void *data) 24258c2ecf20Sopenharmony_ci{ 24268c2ecf20Sopenharmony_ci struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); 24278c2ecf20Sopenharmony_ci 24288c2ecf20Sopenharmony_ci switch (domain->type) { 24298c2ecf20Sopenharmony_ci case IOMMU_DOMAIN_UNMANAGED: 24308c2ecf20Sopenharmony_ci switch (attr) { 24318c2ecf20Sopenharmony_ci case DOMAIN_ATTR_NESTING: 24328c2ecf20Sopenharmony_ci *(int *)data = (smmu_domain->stage == ARM_SMMU_DOMAIN_NESTED); 24338c2ecf20Sopenharmony_ci return 0; 24348c2ecf20Sopenharmony_ci default: 24358c2ecf20Sopenharmony_ci return -ENODEV; 24368c2ecf20Sopenharmony_ci } 24378c2ecf20Sopenharmony_ci break; 24388c2ecf20Sopenharmony_ci case IOMMU_DOMAIN_DMA: 24398c2ecf20Sopenharmony_ci switch (attr) { 24408c2ecf20Sopenharmony_ci case DOMAIN_ATTR_DMA_USE_FLUSH_QUEUE: 24418c2ecf20Sopenharmony_ci *(int *)data = smmu_domain->non_strict; 24428c2ecf20Sopenharmony_ci return 0; 24438c2ecf20Sopenharmony_ci default: 24448c2ecf20Sopenharmony_ci return -ENODEV; 24458c2ecf20Sopenharmony_ci } 24468c2ecf20Sopenharmony_ci break; 24478c2ecf20Sopenharmony_ci default: 24488c2ecf20Sopenharmony_ci return -EINVAL; 24498c2ecf20Sopenharmony_ci } 24508c2ecf20Sopenharmony_ci} 24518c2ecf20Sopenharmony_ci 24528c2ecf20Sopenharmony_cistatic int arm_smmu_domain_set_attr(struct iommu_domain *domain, 24538c2ecf20Sopenharmony_ci enum iommu_attr attr, void *data) 24548c2ecf20Sopenharmony_ci{ 24558c2ecf20Sopenharmony_ci int ret = 0; 24568c2ecf20Sopenharmony_ci struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); 24578c2ecf20Sopenharmony_ci 24588c2ecf20Sopenharmony_ci mutex_lock(&smmu_domain->init_mutex); 24598c2ecf20Sopenharmony_ci 24608c2ecf20Sopenharmony_ci switch (domain->type) { 24618c2ecf20Sopenharmony_ci case IOMMU_DOMAIN_UNMANAGED: 24628c2ecf20Sopenharmony_ci switch (attr) { 24638c2ecf20Sopenharmony_ci case DOMAIN_ATTR_NESTING: 24648c2ecf20Sopenharmony_ci if (smmu_domain->smmu) { 24658c2ecf20Sopenharmony_ci ret = -EPERM; 24668c2ecf20Sopenharmony_ci goto out_unlock; 24678c2ecf20Sopenharmony_ci } 24688c2ecf20Sopenharmony_ci 24698c2ecf20Sopenharmony_ci if (*(int *)data) 24708c2ecf20Sopenharmony_ci smmu_domain->stage = ARM_SMMU_DOMAIN_NESTED; 24718c2ecf20Sopenharmony_ci else 24728c2ecf20Sopenharmony_ci smmu_domain->stage = ARM_SMMU_DOMAIN_S1; 24738c2ecf20Sopenharmony_ci break; 24748c2ecf20Sopenharmony_ci default: 24758c2ecf20Sopenharmony_ci ret = -ENODEV; 24768c2ecf20Sopenharmony_ci } 24778c2ecf20Sopenharmony_ci break; 24788c2ecf20Sopenharmony_ci case IOMMU_DOMAIN_DMA: 24798c2ecf20Sopenharmony_ci switch(attr) { 24808c2ecf20Sopenharmony_ci case DOMAIN_ATTR_DMA_USE_FLUSH_QUEUE: 24818c2ecf20Sopenharmony_ci smmu_domain->non_strict = *(int *)data; 24828c2ecf20Sopenharmony_ci break; 24838c2ecf20Sopenharmony_ci default: 24848c2ecf20Sopenharmony_ci ret = -ENODEV; 24858c2ecf20Sopenharmony_ci } 24868c2ecf20Sopenharmony_ci break; 24878c2ecf20Sopenharmony_ci default: 24888c2ecf20Sopenharmony_ci ret = -EINVAL; 24898c2ecf20Sopenharmony_ci } 24908c2ecf20Sopenharmony_ci 24918c2ecf20Sopenharmony_ciout_unlock: 24928c2ecf20Sopenharmony_ci mutex_unlock(&smmu_domain->init_mutex); 24938c2ecf20Sopenharmony_ci return ret; 24948c2ecf20Sopenharmony_ci} 24958c2ecf20Sopenharmony_ci 24968c2ecf20Sopenharmony_cistatic int arm_smmu_of_xlate(struct device *dev, struct of_phandle_args *args) 24978c2ecf20Sopenharmony_ci{ 24988c2ecf20Sopenharmony_ci return iommu_fwspec_add_ids(dev, args->args, 1); 24998c2ecf20Sopenharmony_ci} 25008c2ecf20Sopenharmony_ci 25018c2ecf20Sopenharmony_cistatic void arm_smmu_get_resv_regions(struct device *dev, 25028c2ecf20Sopenharmony_ci struct list_head *head) 25038c2ecf20Sopenharmony_ci{ 25048c2ecf20Sopenharmony_ci struct iommu_resv_region *region; 25058c2ecf20Sopenharmony_ci int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO; 25068c2ecf20Sopenharmony_ci 25078c2ecf20Sopenharmony_ci region = iommu_alloc_resv_region(MSI_IOVA_BASE, MSI_IOVA_LENGTH, 25088c2ecf20Sopenharmony_ci prot, IOMMU_RESV_SW_MSI); 25098c2ecf20Sopenharmony_ci if (!region) 25108c2ecf20Sopenharmony_ci return; 25118c2ecf20Sopenharmony_ci 25128c2ecf20Sopenharmony_ci list_add_tail(®ion->list, head); 25138c2ecf20Sopenharmony_ci 25148c2ecf20Sopenharmony_ci iommu_dma_get_resv_regions(dev, head); 25158c2ecf20Sopenharmony_ci} 25168c2ecf20Sopenharmony_ci 25178c2ecf20Sopenharmony_cistatic bool arm_smmu_dev_has_feature(struct device *dev, 25188c2ecf20Sopenharmony_ci enum iommu_dev_features feat) 25198c2ecf20Sopenharmony_ci{ 25208c2ecf20Sopenharmony_ci struct arm_smmu_master *master = dev_iommu_priv_get(dev); 25218c2ecf20Sopenharmony_ci 25228c2ecf20Sopenharmony_ci if (!master) 25238c2ecf20Sopenharmony_ci return false; 25248c2ecf20Sopenharmony_ci 25258c2ecf20Sopenharmony_ci switch (feat) { 25268c2ecf20Sopenharmony_ci case IOMMU_DEV_FEAT_SVA: 25278c2ecf20Sopenharmony_ci return arm_smmu_master_sva_supported(master); 25288c2ecf20Sopenharmony_ci default: 25298c2ecf20Sopenharmony_ci return false; 25308c2ecf20Sopenharmony_ci } 25318c2ecf20Sopenharmony_ci} 25328c2ecf20Sopenharmony_ci 25338c2ecf20Sopenharmony_cistatic bool arm_smmu_dev_feature_enabled(struct device *dev, 25348c2ecf20Sopenharmony_ci enum iommu_dev_features feat) 25358c2ecf20Sopenharmony_ci{ 25368c2ecf20Sopenharmony_ci struct arm_smmu_master *master = dev_iommu_priv_get(dev); 25378c2ecf20Sopenharmony_ci 25388c2ecf20Sopenharmony_ci if (!master) 25398c2ecf20Sopenharmony_ci return false; 25408c2ecf20Sopenharmony_ci 25418c2ecf20Sopenharmony_ci switch (feat) { 25428c2ecf20Sopenharmony_ci case IOMMU_DEV_FEAT_SVA: 25438c2ecf20Sopenharmony_ci return arm_smmu_master_sva_enabled(master); 25448c2ecf20Sopenharmony_ci default: 25458c2ecf20Sopenharmony_ci return false; 25468c2ecf20Sopenharmony_ci } 25478c2ecf20Sopenharmony_ci} 25488c2ecf20Sopenharmony_ci 25498c2ecf20Sopenharmony_cistatic int arm_smmu_dev_enable_feature(struct device *dev, 25508c2ecf20Sopenharmony_ci enum iommu_dev_features feat) 25518c2ecf20Sopenharmony_ci{ 25528c2ecf20Sopenharmony_ci if (!arm_smmu_dev_has_feature(dev, feat)) 25538c2ecf20Sopenharmony_ci return -ENODEV; 25548c2ecf20Sopenharmony_ci 25558c2ecf20Sopenharmony_ci if (arm_smmu_dev_feature_enabled(dev, feat)) 25568c2ecf20Sopenharmony_ci return -EBUSY; 25578c2ecf20Sopenharmony_ci 25588c2ecf20Sopenharmony_ci switch (feat) { 25598c2ecf20Sopenharmony_ci case IOMMU_DEV_FEAT_SVA: 25608c2ecf20Sopenharmony_ci return arm_smmu_master_enable_sva(dev_iommu_priv_get(dev)); 25618c2ecf20Sopenharmony_ci default: 25628c2ecf20Sopenharmony_ci return -EINVAL; 25638c2ecf20Sopenharmony_ci } 25648c2ecf20Sopenharmony_ci} 25658c2ecf20Sopenharmony_ci 25668c2ecf20Sopenharmony_cistatic int arm_smmu_dev_disable_feature(struct device *dev, 25678c2ecf20Sopenharmony_ci enum iommu_dev_features feat) 25688c2ecf20Sopenharmony_ci{ 25698c2ecf20Sopenharmony_ci if (!arm_smmu_dev_feature_enabled(dev, feat)) 25708c2ecf20Sopenharmony_ci return -EINVAL; 25718c2ecf20Sopenharmony_ci 25728c2ecf20Sopenharmony_ci switch (feat) { 25738c2ecf20Sopenharmony_ci case IOMMU_DEV_FEAT_SVA: 25748c2ecf20Sopenharmony_ci return arm_smmu_master_disable_sva(dev_iommu_priv_get(dev)); 25758c2ecf20Sopenharmony_ci default: 25768c2ecf20Sopenharmony_ci return -EINVAL; 25778c2ecf20Sopenharmony_ci } 25788c2ecf20Sopenharmony_ci} 25798c2ecf20Sopenharmony_ci 25808c2ecf20Sopenharmony_cistatic struct iommu_ops arm_smmu_ops = { 25818c2ecf20Sopenharmony_ci .capable = arm_smmu_capable, 25828c2ecf20Sopenharmony_ci .domain_alloc = arm_smmu_domain_alloc, 25838c2ecf20Sopenharmony_ci .domain_free = arm_smmu_domain_free, 25848c2ecf20Sopenharmony_ci .attach_dev = arm_smmu_attach_dev, 25858c2ecf20Sopenharmony_ci .map = arm_smmu_map, 25868c2ecf20Sopenharmony_ci .unmap = arm_smmu_unmap, 25878c2ecf20Sopenharmony_ci .flush_iotlb_all = arm_smmu_flush_iotlb_all, 25888c2ecf20Sopenharmony_ci .iotlb_sync = arm_smmu_iotlb_sync, 25898c2ecf20Sopenharmony_ci .iova_to_phys = arm_smmu_iova_to_phys, 25908c2ecf20Sopenharmony_ci .probe_device = arm_smmu_probe_device, 25918c2ecf20Sopenharmony_ci .release_device = arm_smmu_release_device, 25928c2ecf20Sopenharmony_ci .device_group = arm_smmu_device_group, 25938c2ecf20Sopenharmony_ci .domain_get_attr = arm_smmu_domain_get_attr, 25948c2ecf20Sopenharmony_ci .domain_set_attr = arm_smmu_domain_set_attr, 25958c2ecf20Sopenharmony_ci .of_xlate = arm_smmu_of_xlate, 25968c2ecf20Sopenharmony_ci .get_resv_regions = arm_smmu_get_resv_regions, 25978c2ecf20Sopenharmony_ci .put_resv_regions = generic_iommu_put_resv_regions, 25988c2ecf20Sopenharmony_ci .dev_has_feat = arm_smmu_dev_has_feature, 25998c2ecf20Sopenharmony_ci .dev_feat_enabled = arm_smmu_dev_feature_enabled, 26008c2ecf20Sopenharmony_ci .dev_enable_feat = arm_smmu_dev_enable_feature, 26018c2ecf20Sopenharmony_ci .dev_disable_feat = arm_smmu_dev_disable_feature, 26028c2ecf20Sopenharmony_ci .pgsize_bitmap = -1UL, /* Restricted during device attach */ 26038c2ecf20Sopenharmony_ci}; 26048c2ecf20Sopenharmony_ci 26058c2ecf20Sopenharmony_ci/* Probing and initialisation functions */ 26068c2ecf20Sopenharmony_cistatic int arm_smmu_init_one_queue(struct arm_smmu_device *smmu, 26078c2ecf20Sopenharmony_ci struct arm_smmu_queue *q, 26088c2ecf20Sopenharmony_ci unsigned long prod_off, 26098c2ecf20Sopenharmony_ci unsigned long cons_off, 26108c2ecf20Sopenharmony_ci size_t dwords, const char *name) 26118c2ecf20Sopenharmony_ci{ 26128c2ecf20Sopenharmony_ci size_t qsz; 26138c2ecf20Sopenharmony_ci 26148c2ecf20Sopenharmony_ci do { 26158c2ecf20Sopenharmony_ci qsz = ((1 << q->llq.max_n_shift) * dwords) << 3; 26168c2ecf20Sopenharmony_ci q->base = dmam_alloc_coherent(smmu->dev, qsz, &q->base_dma, 26178c2ecf20Sopenharmony_ci GFP_KERNEL); 26188c2ecf20Sopenharmony_ci if (q->base || qsz < PAGE_SIZE) 26198c2ecf20Sopenharmony_ci break; 26208c2ecf20Sopenharmony_ci 26218c2ecf20Sopenharmony_ci q->llq.max_n_shift--; 26228c2ecf20Sopenharmony_ci } while (1); 26238c2ecf20Sopenharmony_ci 26248c2ecf20Sopenharmony_ci if (!q->base) { 26258c2ecf20Sopenharmony_ci dev_err(smmu->dev, 26268c2ecf20Sopenharmony_ci "failed to allocate queue (0x%zx bytes) for %s\n", 26278c2ecf20Sopenharmony_ci qsz, name); 26288c2ecf20Sopenharmony_ci return -ENOMEM; 26298c2ecf20Sopenharmony_ci } 26308c2ecf20Sopenharmony_ci 26318c2ecf20Sopenharmony_ci if (!WARN_ON(q->base_dma & (qsz - 1))) { 26328c2ecf20Sopenharmony_ci dev_info(smmu->dev, "allocated %u entries for %s\n", 26338c2ecf20Sopenharmony_ci 1 << q->llq.max_n_shift, name); 26348c2ecf20Sopenharmony_ci } 26358c2ecf20Sopenharmony_ci 26368c2ecf20Sopenharmony_ci q->prod_reg = arm_smmu_page1_fixup(prod_off, smmu); 26378c2ecf20Sopenharmony_ci q->cons_reg = arm_smmu_page1_fixup(cons_off, smmu); 26388c2ecf20Sopenharmony_ci q->ent_dwords = dwords; 26398c2ecf20Sopenharmony_ci 26408c2ecf20Sopenharmony_ci q->q_base = Q_BASE_RWA; 26418c2ecf20Sopenharmony_ci q->q_base |= q->base_dma & Q_BASE_ADDR_MASK; 26428c2ecf20Sopenharmony_ci q->q_base |= FIELD_PREP(Q_BASE_LOG2SIZE, q->llq.max_n_shift); 26438c2ecf20Sopenharmony_ci 26448c2ecf20Sopenharmony_ci q->llq.prod = q->llq.cons = 0; 26458c2ecf20Sopenharmony_ci return 0; 26468c2ecf20Sopenharmony_ci} 26478c2ecf20Sopenharmony_ci 26488c2ecf20Sopenharmony_cistatic void arm_smmu_cmdq_free_bitmap(void *data) 26498c2ecf20Sopenharmony_ci{ 26508c2ecf20Sopenharmony_ci unsigned long *bitmap = data; 26518c2ecf20Sopenharmony_ci bitmap_free(bitmap); 26528c2ecf20Sopenharmony_ci} 26538c2ecf20Sopenharmony_ci 26548c2ecf20Sopenharmony_cistatic int arm_smmu_cmdq_init(struct arm_smmu_device *smmu) 26558c2ecf20Sopenharmony_ci{ 26568c2ecf20Sopenharmony_ci int ret = 0; 26578c2ecf20Sopenharmony_ci struct arm_smmu_cmdq *cmdq = &smmu->cmdq; 26588c2ecf20Sopenharmony_ci unsigned int nents = 1 << cmdq->q.llq.max_n_shift; 26598c2ecf20Sopenharmony_ci atomic_long_t *bitmap; 26608c2ecf20Sopenharmony_ci 26618c2ecf20Sopenharmony_ci atomic_set(&cmdq->owner_prod, 0); 26628c2ecf20Sopenharmony_ci atomic_set(&cmdq->lock, 0); 26638c2ecf20Sopenharmony_ci 26648c2ecf20Sopenharmony_ci bitmap = (atomic_long_t *)bitmap_zalloc(nents, GFP_KERNEL); 26658c2ecf20Sopenharmony_ci if (!bitmap) { 26668c2ecf20Sopenharmony_ci dev_err(smmu->dev, "failed to allocate cmdq bitmap\n"); 26678c2ecf20Sopenharmony_ci ret = -ENOMEM; 26688c2ecf20Sopenharmony_ci } else { 26698c2ecf20Sopenharmony_ci cmdq->valid_map = bitmap; 26708c2ecf20Sopenharmony_ci devm_add_action(smmu->dev, arm_smmu_cmdq_free_bitmap, bitmap); 26718c2ecf20Sopenharmony_ci } 26728c2ecf20Sopenharmony_ci 26738c2ecf20Sopenharmony_ci return ret; 26748c2ecf20Sopenharmony_ci} 26758c2ecf20Sopenharmony_ci 26768c2ecf20Sopenharmony_cistatic int arm_smmu_init_queues(struct arm_smmu_device *smmu) 26778c2ecf20Sopenharmony_ci{ 26788c2ecf20Sopenharmony_ci int ret; 26798c2ecf20Sopenharmony_ci 26808c2ecf20Sopenharmony_ci /* cmdq */ 26818c2ecf20Sopenharmony_ci ret = arm_smmu_init_one_queue(smmu, &smmu->cmdq.q, ARM_SMMU_CMDQ_PROD, 26828c2ecf20Sopenharmony_ci ARM_SMMU_CMDQ_CONS, CMDQ_ENT_DWORDS, 26838c2ecf20Sopenharmony_ci "cmdq"); 26848c2ecf20Sopenharmony_ci if (ret) 26858c2ecf20Sopenharmony_ci return ret; 26868c2ecf20Sopenharmony_ci 26878c2ecf20Sopenharmony_ci ret = arm_smmu_cmdq_init(smmu); 26888c2ecf20Sopenharmony_ci if (ret) 26898c2ecf20Sopenharmony_ci return ret; 26908c2ecf20Sopenharmony_ci 26918c2ecf20Sopenharmony_ci /* evtq */ 26928c2ecf20Sopenharmony_ci ret = arm_smmu_init_one_queue(smmu, &smmu->evtq.q, ARM_SMMU_EVTQ_PROD, 26938c2ecf20Sopenharmony_ci ARM_SMMU_EVTQ_CONS, EVTQ_ENT_DWORDS, 26948c2ecf20Sopenharmony_ci "evtq"); 26958c2ecf20Sopenharmony_ci if (ret) 26968c2ecf20Sopenharmony_ci return ret; 26978c2ecf20Sopenharmony_ci 26988c2ecf20Sopenharmony_ci /* priq */ 26998c2ecf20Sopenharmony_ci if (!(smmu->features & ARM_SMMU_FEAT_PRI)) 27008c2ecf20Sopenharmony_ci return 0; 27018c2ecf20Sopenharmony_ci 27028c2ecf20Sopenharmony_ci return arm_smmu_init_one_queue(smmu, &smmu->priq.q, ARM_SMMU_PRIQ_PROD, 27038c2ecf20Sopenharmony_ci ARM_SMMU_PRIQ_CONS, PRIQ_ENT_DWORDS, 27048c2ecf20Sopenharmony_ci "priq"); 27058c2ecf20Sopenharmony_ci} 27068c2ecf20Sopenharmony_ci 27078c2ecf20Sopenharmony_cistatic int arm_smmu_init_l1_strtab(struct arm_smmu_device *smmu) 27088c2ecf20Sopenharmony_ci{ 27098c2ecf20Sopenharmony_ci unsigned int i; 27108c2ecf20Sopenharmony_ci struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg; 27118c2ecf20Sopenharmony_ci size_t size = sizeof(*cfg->l1_desc) * cfg->num_l1_ents; 27128c2ecf20Sopenharmony_ci void *strtab = smmu->strtab_cfg.strtab; 27138c2ecf20Sopenharmony_ci 27148c2ecf20Sopenharmony_ci cfg->l1_desc = devm_kzalloc(smmu->dev, size, GFP_KERNEL); 27158c2ecf20Sopenharmony_ci if (!cfg->l1_desc) { 27168c2ecf20Sopenharmony_ci dev_err(smmu->dev, "failed to allocate l1 stream table desc\n"); 27178c2ecf20Sopenharmony_ci return -ENOMEM; 27188c2ecf20Sopenharmony_ci } 27198c2ecf20Sopenharmony_ci 27208c2ecf20Sopenharmony_ci for (i = 0; i < cfg->num_l1_ents; ++i) { 27218c2ecf20Sopenharmony_ci arm_smmu_write_strtab_l1_desc(strtab, &cfg->l1_desc[i]); 27228c2ecf20Sopenharmony_ci strtab += STRTAB_L1_DESC_DWORDS << 3; 27238c2ecf20Sopenharmony_ci } 27248c2ecf20Sopenharmony_ci 27258c2ecf20Sopenharmony_ci return 0; 27268c2ecf20Sopenharmony_ci} 27278c2ecf20Sopenharmony_ci 27288c2ecf20Sopenharmony_cistatic int arm_smmu_init_strtab_2lvl(struct arm_smmu_device *smmu) 27298c2ecf20Sopenharmony_ci{ 27308c2ecf20Sopenharmony_ci void *strtab; 27318c2ecf20Sopenharmony_ci u64 reg; 27328c2ecf20Sopenharmony_ci u32 size, l1size; 27338c2ecf20Sopenharmony_ci struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg; 27348c2ecf20Sopenharmony_ci 27358c2ecf20Sopenharmony_ci /* Calculate the L1 size, capped to the SIDSIZE. */ 27368c2ecf20Sopenharmony_ci size = STRTAB_L1_SZ_SHIFT - (ilog2(STRTAB_L1_DESC_DWORDS) + 3); 27378c2ecf20Sopenharmony_ci size = min(size, smmu->sid_bits - STRTAB_SPLIT); 27388c2ecf20Sopenharmony_ci cfg->num_l1_ents = 1 << size; 27398c2ecf20Sopenharmony_ci 27408c2ecf20Sopenharmony_ci size += STRTAB_SPLIT; 27418c2ecf20Sopenharmony_ci if (size < smmu->sid_bits) 27428c2ecf20Sopenharmony_ci dev_warn(smmu->dev, 27438c2ecf20Sopenharmony_ci "2-level strtab only covers %u/%u bits of SID\n", 27448c2ecf20Sopenharmony_ci size, smmu->sid_bits); 27458c2ecf20Sopenharmony_ci 27468c2ecf20Sopenharmony_ci l1size = cfg->num_l1_ents * (STRTAB_L1_DESC_DWORDS << 3); 27478c2ecf20Sopenharmony_ci strtab = dmam_alloc_coherent(smmu->dev, l1size, &cfg->strtab_dma, 27488c2ecf20Sopenharmony_ci GFP_KERNEL); 27498c2ecf20Sopenharmony_ci if (!strtab) { 27508c2ecf20Sopenharmony_ci dev_err(smmu->dev, 27518c2ecf20Sopenharmony_ci "failed to allocate l1 stream table (%u bytes)\n", 27528c2ecf20Sopenharmony_ci l1size); 27538c2ecf20Sopenharmony_ci return -ENOMEM; 27548c2ecf20Sopenharmony_ci } 27558c2ecf20Sopenharmony_ci cfg->strtab = strtab; 27568c2ecf20Sopenharmony_ci 27578c2ecf20Sopenharmony_ci /* Configure strtab_base_cfg for 2 levels */ 27588c2ecf20Sopenharmony_ci reg = FIELD_PREP(STRTAB_BASE_CFG_FMT, STRTAB_BASE_CFG_FMT_2LVL); 27598c2ecf20Sopenharmony_ci reg |= FIELD_PREP(STRTAB_BASE_CFG_LOG2SIZE, size); 27608c2ecf20Sopenharmony_ci reg |= FIELD_PREP(STRTAB_BASE_CFG_SPLIT, STRTAB_SPLIT); 27618c2ecf20Sopenharmony_ci cfg->strtab_base_cfg = reg; 27628c2ecf20Sopenharmony_ci 27638c2ecf20Sopenharmony_ci return arm_smmu_init_l1_strtab(smmu); 27648c2ecf20Sopenharmony_ci} 27658c2ecf20Sopenharmony_ci 27668c2ecf20Sopenharmony_cistatic int arm_smmu_init_strtab_linear(struct arm_smmu_device *smmu) 27678c2ecf20Sopenharmony_ci{ 27688c2ecf20Sopenharmony_ci void *strtab; 27698c2ecf20Sopenharmony_ci u64 reg; 27708c2ecf20Sopenharmony_ci u32 size; 27718c2ecf20Sopenharmony_ci struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg; 27728c2ecf20Sopenharmony_ci 27738c2ecf20Sopenharmony_ci size = (1 << smmu->sid_bits) * (STRTAB_STE_DWORDS << 3); 27748c2ecf20Sopenharmony_ci strtab = dmam_alloc_coherent(smmu->dev, size, &cfg->strtab_dma, 27758c2ecf20Sopenharmony_ci GFP_KERNEL); 27768c2ecf20Sopenharmony_ci if (!strtab) { 27778c2ecf20Sopenharmony_ci dev_err(smmu->dev, 27788c2ecf20Sopenharmony_ci "failed to allocate linear stream table (%u bytes)\n", 27798c2ecf20Sopenharmony_ci size); 27808c2ecf20Sopenharmony_ci return -ENOMEM; 27818c2ecf20Sopenharmony_ci } 27828c2ecf20Sopenharmony_ci cfg->strtab = strtab; 27838c2ecf20Sopenharmony_ci cfg->num_l1_ents = 1 << smmu->sid_bits; 27848c2ecf20Sopenharmony_ci 27858c2ecf20Sopenharmony_ci /* Configure strtab_base_cfg for a linear table covering all SIDs */ 27868c2ecf20Sopenharmony_ci reg = FIELD_PREP(STRTAB_BASE_CFG_FMT, STRTAB_BASE_CFG_FMT_LINEAR); 27878c2ecf20Sopenharmony_ci reg |= FIELD_PREP(STRTAB_BASE_CFG_LOG2SIZE, smmu->sid_bits); 27888c2ecf20Sopenharmony_ci cfg->strtab_base_cfg = reg; 27898c2ecf20Sopenharmony_ci 27908c2ecf20Sopenharmony_ci arm_smmu_init_bypass_stes(strtab, cfg->num_l1_ents); 27918c2ecf20Sopenharmony_ci return 0; 27928c2ecf20Sopenharmony_ci} 27938c2ecf20Sopenharmony_ci 27948c2ecf20Sopenharmony_cistatic int arm_smmu_init_strtab(struct arm_smmu_device *smmu) 27958c2ecf20Sopenharmony_ci{ 27968c2ecf20Sopenharmony_ci u64 reg; 27978c2ecf20Sopenharmony_ci int ret; 27988c2ecf20Sopenharmony_ci 27998c2ecf20Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB) 28008c2ecf20Sopenharmony_ci ret = arm_smmu_init_strtab_2lvl(smmu); 28018c2ecf20Sopenharmony_ci else 28028c2ecf20Sopenharmony_ci ret = arm_smmu_init_strtab_linear(smmu); 28038c2ecf20Sopenharmony_ci 28048c2ecf20Sopenharmony_ci if (ret) 28058c2ecf20Sopenharmony_ci return ret; 28068c2ecf20Sopenharmony_ci 28078c2ecf20Sopenharmony_ci /* Set the strtab base address */ 28088c2ecf20Sopenharmony_ci reg = smmu->strtab_cfg.strtab_dma & STRTAB_BASE_ADDR_MASK; 28098c2ecf20Sopenharmony_ci reg |= STRTAB_BASE_RA; 28108c2ecf20Sopenharmony_ci smmu->strtab_cfg.strtab_base = reg; 28118c2ecf20Sopenharmony_ci 28128c2ecf20Sopenharmony_ci /* Allocate the first VMID for stage-2 bypass STEs */ 28138c2ecf20Sopenharmony_ci set_bit(0, smmu->vmid_map); 28148c2ecf20Sopenharmony_ci return 0; 28158c2ecf20Sopenharmony_ci} 28168c2ecf20Sopenharmony_ci 28178c2ecf20Sopenharmony_cistatic int arm_smmu_init_structures(struct arm_smmu_device *smmu) 28188c2ecf20Sopenharmony_ci{ 28198c2ecf20Sopenharmony_ci int ret; 28208c2ecf20Sopenharmony_ci 28218c2ecf20Sopenharmony_ci ret = arm_smmu_init_queues(smmu); 28228c2ecf20Sopenharmony_ci if (ret) 28238c2ecf20Sopenharmony_ci return ret; 28248c2ecf20Sopenharmony_ci 28258c2ecf20Sopenharmony_ci return arm_smmu_init_strtab(smmu); 28268c2ecf20Sopenharmony_ci} 28278c2ecf20Sopenharmony_ci 28288c2ecf20Sopenharmony_cistatic int arm_smmu_write_reg_sync(struct arm_smmu_device *smmu, u32 val, 28298c2ecf20Sopenharmony_ci unsigned int reg_off, unsigned int ack_off) 28308c2ecf20Sopenharmony_ci{ 28318c2ecf20Sopenharmony_ci u32 reg; 28328c2ecf20Sopenharmony_ci 28338c2ecf20Sopenharmony_ci writel_relaxed(val, smmu->base + reg_off); 28348c2ecf20Sopenharmony_ci return readl_relaxed_poll_timeout(smmu->base + ack_off, reg, reg == val, 28358c2ecf20Sopenharmony_ci 1, ARM_SMMU_POLL_TIMEOUT_US); 28368c2ecf20Sopenharmony_ci} 28378c2ecf20Sopenharmony_ci 28388c2ecf20Sopenharmony_ci/* GBPA is "special" */ 28398c2ecf20Sopenharmony_cistatic int arm_smmu_update_gbpa(struct arm_smmu_device *smmu, u32 set, u32 clr) 28408c2ecf20Sopenharmony_ci{ 28418c2ecf20Sopenharmony_ci int ret; 28428c2ecf20Sopenharmony_ci u32 reg, __iomem *gbpa = smmu->base + ARM_SMMU_GBPA; 28438c2ecf20Sopenharmony_ci 28448c2ecf20Sopenharmony_ci ret = readl_relaxed_poll_timeout(gbpa, reg, !(reg & GBPA_UPDATE), 28458c2ecf20Sopenharmony_ci 1, ARM_SMMU_POLL_TIMEOUT_US); 28468c2ecf20Sopenharmony_ci if (ret) 28478c2ecf20Sopenharmony_ci return ret; 28488c2ecf20Sopenharmony_ci 28498c2ecf20Sopenharmony_ci reg &= ~clr; 28508c2ecf20Sopenharmony_ci reg |= set; 28518c2ecf20Sopenharmony_ci writel_relaxed(reg | GBPA_UPDATE, gbpa); 28528c2ecf20Sopenharmony_ci ret = readl_relaxed_poll_timeout(gbpa, reg, !(reg & GBPA_UPDATE), 28538c2ecf20Sopenharmony_ci 1, ARM_SMMU_POLL_TIMEOUT_US); 28548c2ecf20Sopenharmony_ci 28558c2ecf20Sopenharmony_ci if (ret) 28568c2ecf20Sopenharmony_ci dev_err(smmu->dev, "GBPA not responding to update\n"); 28578c2ecf20Sopenharmony_ci return ret; 28588c2ecf20Sopenharmony_ci} 28598c2ecf20Sopenharmony_ci 28608c2ecf20Sopenharmony_cistatic void arm_smmu_free_msis(void *data) 28618c2ecf20Sopenharmony_ci{ 28628c2ecf20Sopenharmony_ci struct device *dev = data; 28638c2ecf20Sopenharmony_ci platform_msi_domain_free_irqs(dev); 28648c2ecf20Sopenharmony_ci} 28658c2ecf20Sopenharmony_ci 28668c2ecf20Sopenharmony_cistatic void arm_smmu_write_msi_msg(struct msi_desc *desc, struct msi_msg *msg) 28678c2ecf20Sopenharmony_ci{ 28688c2ecf20Sopenharmony_ci phys_addr_t doorbell; 28698c2ecf20Sopenharmony_ci struct device *dev = msi_desc_to_dev(desc); 28708c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu = dev_get_drvdata(dev); 28718c2ecf20Sopenharmony_ci phys_addr_t *cfg = arm_smmu_msi_cfg[desc->platform.msi_index]; 28728c2ecf20Sopenharmony_ci 28738c2ecf20Sopenharmony_ci doorbell = (((u64)msg->address_hi) << 32) | msg->address_lo; 28748c2ecf20Sopenharmony_ci doorbell &= MSI_CFG0_ADDR_MASK; 28758c2ecf20Sopenharmony_ci 28768c2ecf20Sopenharmony_ci writeq_relaxed(doorbell, smmu->base + cfg[0]); 28778c2ecf20Sopenharmony_ci writel_relaxed(msg->data, smmu->base + cfg[1]); 28788c2ecf20Sopenharmony_ci writel_relaxed(ARM_SMMU_MEMATTR_DEVICE_nGnRE, smmu->base + cfg[2]); 28798c2ecf20Sopenharmony_ci} 28808c2ecf20Sopenharmony_ci 28818c2ecf20Sopenharmony_cistatic void arm_smmu_setup_msis(struct arm_smmu_device *smmu) 28828c2ecf20Sopenharmony_ci{ 28838c2ecf20Sopenharmony_ci struct msi_desc *desc; 28848c2ecf20Sopenharmony_ci int ret, nvec = ARM_SMMU_MAX_MSIS; 28858c2ecf20Sopenharmony_ci struct device *dev = smmu->dev; 28868c2ecf20Sopenharmony_ci 28878c2ecf20Sopenharmony_ci /* Clear the MSI address regs */ 28888c2ecf20Sopenharmony_ci writeq_relaxed(0, smmu->base + ARM_SMMU_GERROR_IRQ_CFG0); 28898c2ecf20Sopenharmony_ci writeq_relaxed(0, smmu->base + ARM_SMMU_EVTQ_IRQ_CFG0); 28908c2ecf20Sopenharmony_ci 28918c2ecf20Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_PRI) 28928c2ecf20Sopenharmony_ci writeq_relaxed(0, smmu->base + ARM_SMMU_PRIQ_IRQ_CFG0); 28938c2ecf20Sopenharmony_ci else 28948c2ecf20Sopenharmony_ci nvec--; 28958c2ecf20Sopenharmony_ci 28968c2ecf20Sopenharmony_ci if (!(smmu->features & ARM_SMMU_FEAT_MSI)) 28978c2ecf20Sopenharmony_ci return; 28988c2ecf20Sopenharmony_ci 28998c2ecf20Sopenharmony_ci if (!dev->msi_domain) { 29008c2ecf20Sopenharmony_ci dev_info(smmu->dev, "msi_domain absent - falling back to wired irqs\n"); 29018c2ecf20Sopenharmony_ci return; 29028c2ecf20Sopenharmony_ci } 29038c2ecf20Sopenharmony_ci 29048c2ecf20Sopenharmony_ci /* Allocate MSIs for evtq, gerror and priq. Ignore cmdq */ 29058c2ecf20Sopenharmony_ci ret = platform_msi_domain_alloc_irqs(dev, nvec, arm_smmu_write_msi_msg); 29068c2ecf20Sopenharmony_ci if (ret) { 29078c2ecf20Sopenharmony_ci dev_warn(dev, "failed to allocate MSIs - falling back to wired irqs\n"); 29088c2ecf20Sopenharmony_ci return; 29098c2ecf20Sopenharmony_ci } 29108c2ecf20Sopenharmony_ci 29118c2ecf20Sopenharmony_ci for_each_msi_entry(desc, dev) { 29128c2ecf20Sopenharmony_ci switch (desc->platform.msi_index) { 29138c2ecf20Sopenharmony_ci case EVTQ_MSI_INDEX: 29148c2ecf20Sopenharmony_ci smmu->evtq.q.irq = desc->irq; 29158c2ecf20Sopenharmony_ci break; 29168c2ecf20Sopenharmony_ci case GERROR_MSI_INDEX: 29178c2ecf20Sopenharmony_ci smmu->gerr_irq = desc->irq; 29188c2ecf20Sopenharmony_ci break; 29198c2ecf20Sopenharmony_ci case PRIQ_MSI_INDEX: 29208c2ecf20Sopenharmony_ci smmu->priq.q.irq = desc->irq; 29218c2ecf20Sopenharmony_ci break; 29228c2ecf20Sopenharmony_ci default: /* Unknown */ 29238c2ecf20Sopenharmony_ci continue; 29248c2ecf20Sopenharmony_ci } 29258c2ecf20Sopenharmony_ci } 29268c2ecf20Sopenharmony_ci 29278c2ecf20Sopenharmony_ci /* Add callback to free MSIs on teardown */ 29288c2ecf20Sopenharmony_ci devm_add_action(dev, arm_smmu_free_msis, dev); 29298c2ecf20Sopenharmony_ci} 29308c2ecf20Sopenharmony_ci 29318c2ecf20Sopenharmony_cistatic void arm_smmu_setup_unique_irqs(struct arm_smmu_device *smmu) 29328c2ecf20Sopenharmony_ci{ 29338c2ecf20Sopenharmony_ci int irq, ret; 29348c2ecf20Sopenharmony_ci 29358c2ecf20Sopenharmony_ci arm_smmu_setup_msis(smmu); 29368c2ecf20Sopenharmony_ci 29378c2ecf20Sopenharmony_ci /* Request interrupt lines */ 29388c2ecf20Sopenharmony_ci irq = smmu->evtq.q.irq; 29398c2ecf20Sopenharmony_ci if (irq) { 29408c2ecf20Sopenharmony_ci ret = devm_request_threaded_irq(smmu->dev, irq, NULL, 29418c2ecf20Sopenharmony_ci arm_smmu_evtq_thread, 29428c2ecf20Sopenharmony_ci IRQF_ONESHOT, 29438c2ecf20Sopenharmony_ci "arm-smmu-v3-evtq", smmu); 29448c2ecf20Sopenharmony_ci if (ret < 0) 29458c2ecf20Sopenharmony_ci dev_warn(smmu->dev, "failed to enable evtq irq\n"); 29468c2ecf20Sopenharmony_ci } else { 29478c2ecf20Sopenharmony_ci dev_warn(smmu->dev, "no evtq irq - events will not be reported!\n"); 29488c2ecf20Sopenharmony_ci } 29498c2ecf20Sopenharmony_ci 29508c2ecf20Sopenharmony_ci irq = smmu->gerr_irq; 29518c2ecf20Sopenharmony_ci if (irq) { 29528c2ecf20Sopenharmony_ci ret = devm_request_irq(smmu->dev, irq, arm_smmu_gerror_handler, 29538c2ecf20Sopenharmony_ci 0, "arm-smmu-v3-gerror", smmu); 29548c2ecf20Sopenharmony_ci if (ret < 0) 29558c2ecf20Sopenharmony_ci dev_warn(smmu->dev, "failed to enable gerror irq\n"); 29568c2ecf20Sopenharmony_ci } else { 29578c2ecf20Sopenharmony_ci dev_warn(smmu->dev, "no gerr irq - errors will not be reported!\n"); 29588c2ecf20Sopenharmony_ci } 29598c2ecf20Sopenharmony_ci 29608c2ecf20Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_PRI) { 29618c2ecf20Sopenharmony_ci irq = smmu->priq.q.irq; 29628c2ecf20Sopenharmony_ci if (irq) { 29638c2ecf20Sopenharmony_ci ret = devm_request_threaded_irq(smmu->dev, irq, NULL, 29648c2ecf20Sopenharmony_ci arm_smmu_priq_thread, 29658c2ecf20Sopenharmony_ci IRQF_ONESHOT, 29668c2ecf20Sopenharmony_ci "arm-smmu-v3-priq", 29678c2ecf20Sopenharmony_ci smmu); 29688c2ecf20Sopenharmony_ci if (ret < 0) 29698c2ecf20Sopenharmony_ci dev_warn(smmu->dev, 29708c2ecf20Sopenharmony_ci "failed to enable priq irq\n"); 29718c2ecf20Sopenharmony_ci } else { 29728c2ecf20Sopenharmony_ci dev_warn(smmu->dev, "no priq irq - PRI will be broken\n"); 29738c2ecf20Sopenharmony_ci } 29748c2ecf20Sopenharmony_ci } 29758c2ecf20Sopenharmony_ci} 29768c2ecf20Sopenharmony_ci 29778c2ecf20Sopenharmony_cistatic int arm_smmu_setup_irqs(struct arm_smmu_device *smmu) 29788c2ecf20Sopenharmony_ci{ 29798c2ecf20Sopenharmony_ci int ret, irq; 29808c2ecf20Sopenharmony_ci u32 irqen_flags = IRQ_CTRL_EVTQ_IRQEN | IRQ_CTRL_GERROR_IRQEN; 29818c2ecf20Sopenharmony_ci 29828c2ecf20Sopenharmony_ci /* Disable IRQs first */ 29838c2ecf20Sopenharmony_ci ret = arm_smmu_write_reg_sync(smmu, 0, ARM_SMMU_IRQ_CTRL, 29848c2ecf20Sopenharmony_ci ARM_SMMU_IRQ_CTRLACK); 29858c2ecf20Sopenharmony_ci if (ret) { 29868c2ecf20Sopenharmony_ci dev_err(smmu->dev, "failed to disable irqs\n"); 29878c2ecf20Sopenharmony_ci return ret; 29888c2ecf20Sopenharmony_ci } 29898c2ecf20Sopenharmony_ci 29908c2ecf20Sopenharmony_ci irq = smmu->combined_irq; 29918c2ecf20Sopenharmony_ci if (irq) { 29928c2ecf20Sopenharmony_ci /* 29938c2ecf20Sopenharmony_ci * Cavium ThunderX2 implementation doesn't support unique irq 29948c2ecf20Sopenharmony_ci * lines. Use a single irq line for all the SMMUv3 interrupts. 29958c2ecf20Sopenharmony_ci */ 29968c2ecf20Sopenharmony_ci ret = devm_request_threaded_irq(smmu->dev, irq, 29978c2ecf20Sopenharmony_ci arm_smmu_combined_irq_handler, 29988c2ecf20Sopenharmony_ci arm_smmu_combined_irq_thread, 29998c2ecf20Sopenharmony_ci IRQF_ONESHOT, 30008c2ecf20Sopenharmony_ci "arm-smmu-v3-combined-irq", smmu); 30018c2ecf20Sopenharmony_ci if (ret < 0) 30028c2ecf20Sopenharmony_ci dev_warn(smmu->dev, "failed to enable combined irq\n"); 30038c2ecf20Sopenharmony_ci } else 30048c2ecf20Sopenharmony_ci arm_smmu_setup_unique_irqs(smmu); 30058c2ecf20Sopenharmony_ci 30068c2ecf20Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_PRI) 30078c2ecf20Sopenharmony_ci irqen_flags |= IRQ_CTRL_PRIQ_IRQEN; 30088c2ecf20Sopenharmony_ci 30098c2ecf20Sopenharmony_ci /* Enable interrupt generation on the SMMU */ 30108c2ecf20Sopenharmony_ci ret = arm_smmu_write_reg_sync(smmu, irqen_flags, 30118c2ecf20Sopenharmony_ci ARM_SMMU_IRQ_CTRL, ARM_SMMU_IRQ_CTRLACK); 30128c2ecf20Sopenharmony_ci if (ret) 30138c2ecf20Sopenharmony_ci dev_warn(smmu->dev, "failed to enable irqs\n"); 30148c2ecf20Sopenharmony_ci 30158c2ecf20Sopenharmony_ci return 0; 30168c2ecf20Sopenharmony_ci} 30178c2ecf20Sopenharmony_ci 30188c2ecf20Sopenharmony_cistatic int arm_smmu_device_disable(struct arm_smmu_device *smmu) 30198c2ecf20Sopenharmony_ci{ 30208c2ecf20Sopenharmony_ci int ret; 30218c2ecf20Sopenharmony_ci 30228c2ecf20Sopenharmony_ci ret = arm_smmu_write_reg_sync(smmu, 0, ARM_SMMU_CR0, ARM_SMMU_CR0ACK); 30238c2ecf20Sopenharmony_ci if (ret) 30248c2ecf20Sopenharmony_ci dev_err(smmu->dev, "failed to clear cr0\n"); 30258c2ecf20Sopenharmony_ci 30268c2ecf20Sopenharmony_ci return ret; 30278c2ecf20Sopenharmony_ci} 30288c2ecf20Sopenharmony_ci 30298c2ecf20Sopenharmony_cistatic int arm_smmu_device_reset(struct arm_smmu_device *smmu, bool bypass) 30308c2ecf20Sopenharmony_ci{ 30318c2ecf20Sopenharmony_ci int ret; 30328c2ecf20Sopenharmony_ci u32 reg, enables; 30338c2ecf20Sopenharmony_ci struct arm_smmu_cmdq_ent cmd; 30348c2ecf20Sopenharmony_ci 30358c2ecf20Sopenharmony_ci /* Clear CR0 and sync (disables SMMU and queue processing) */ 30368c2ecf20Sopenharmony_ci reg = readl_relaxed(smmu->base + ARM_SMMU_CR0); 30378c2ecf20Sopenharmony_ci if (reg & CR0_SMMUEN) { 30388c2ecf20Sopenharmony_ci dev_warn(smmu->dev, "SMMU currently enabled! Resetting...\n"); 30398c2ecf20Sopenharmony_ci WARN_ON(is_kdump_kernel() && !disable_bypass); 30408c2ecf20Sopenharmony_ci arm_smmu_update_gbpa(smmu, GBPA_ABORT, 0); 30418c2ecf20Sopenharmony_ci } 30428c2ecf20Sopenharmony_ci 30438c2ecf20Sopenharmony_ci ret = arm_smmu_device_disable(smmu); 30448c2ecf20Sopenharmony_ci if (ret) 30458c2ecf20Sopenharmony_ci return ret; 30468c2ecf20Sopenharmony_ci 30478c2ecf20Sopenharmony_ci /* CR1 (table and queue memory attributes) */ 30488c2ecf20Sopenharmony_ci reg = FIELD_PREP(CR1_TABLE_SH, ARM_SMMU_SH_ISH) | 30498c2ecf20Sopenharmony_ci FIELD_PREP(CR1_TABLE_OC, CR1_CACHE_WB) | 30508c2ecf20Sopenharmony_ci FIELD_PREP(CR1_TABLE_IC, CR1_CACHE_WB) | 30518c2ecf20Sopenharmony_ci FIELD_PREP(CR1_QUEUE_SH, ARM_SMMU_SH_ISH) | 30528c2ecf20Sopenharmony_ci FIELD_PREP(CR1_QUEUE_OC, CR1_CACHE_WB) | 30538c2ecf20Sopenharmony_ci FIELD_PREP(CR1_QUEUE_IC, CR1_CACHE_WB); 30548c2ecf20Sopenharmony_ci writel_relaxed(reg, smmu->base + ARM_SMMU_CR1); 30558c2ecf20Sopenharmony_ci 30568c2ecf20Sopenharmony_ci /* CR2 (random crap) */ 30578c2ecf20Sopenharmony_ci reg = CR2_PTM | CR2_RECINVSID | CR2_E2H; 30588c2ecf20Sopenharmony_ci writel_relaxed(reg, smmu->base + ARM_SMMU_CR2); 30598c2ecf20Sopenharmony_ci 30608c2ecf20Sopenharmony_ci /* Stream table */ 30618c2ecf20Sopenharmony_ci writeq_relaxed(smmu->strtab_cfg.strtab_base, 30628c2ecf20Sopenharmony_ci smmu->base + ARM_SMMU_STRTAB_BASE); 30638c2ecf20Sopenharmony_ci writel_relaxed(smmu->strtab_cfg.strtab_base_cfg, 30648c2ecf20Sopenharmony_ci smmu->base + ARM_SMMU_STRTAB_BASE_CFG); 30658c2ecf20Sopenharmony_ci 30668c2ecf20Sopenharmony_ci /* Command queue */ 30678c2ecf20Sopenharmony_ci writeq_relaxed(smmu->cmdq.q.q_base, smmu->base + ARM_SMMU_CMDQ_BASE); 30688c2ecf20Sopenharmony_ci writel_relaxed(smmu->cmdq.q.llq.prod, smmu->base + ARM_SMMU_CMDQ_PROD); 30698c2ecf20Sopenharmony_ci writel_relaxed(smmu->cmdq.q.llq.cons, smmu->base + ARM_SMMU_CMDQ_CONS); 30708c2ecf20Sopenharmony_ci 30718c2ecf20Sopenharmony_ci enables = CR0_CMDQEN; 30728c2ecf20Sopenharmony_ci ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0, 30738c2ecf20Sopenharmony_ci ARM_SMMU_CR0ACK); 30748c2ecf20Sopenharmony_ci if (ret) { 30758c2ecf20Sopenharmony_ci dev_err(smmu->dev, "failed to enable command queue\n"); 30768c2ecf20Sopenharmony_ci return ret; 30778c2ecf20Sopenharmony_ci } 30788c2ecf20Sopenharmony_ci 30798c2ecf20Sopenharmony_ci /* Invalidate any cached configuration */ 30808c2ecf20Sopenharmony_ci cmd.opcode = CMDQ_OP_CFGI_ALL; 30818c2ecf20Sopenharmony_ci arm_smmu_cmdq_issue_cmd(smmu, &cmd); 30828c2ecf20Sopenharmony_ci arm_smmu_cmdq_issue_sync(smmu); 30838c2ecf20Sopenharmony_ci 30848c2ecf20Sopenharmony_ci /* Invalidate any stale TLB entries */ 30858c2ecf20Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_HYP) { 30868c2ecf20Sopenharmony_ci cmd.opcode = CMDQ_OP_TLBI_EL2_ALL; 30878c2ecf20Sopenharmony_ci arm_smmu_cmdq_issue_cmd(smmu, &cmd); 30888c2ecf20Sopenharmony_ci } 30898c2ecf20Sopenharmony_ci 30908c2ecf20Sopenharmony_ci cmd.opcode = CMDQ_OP_TLBI_NSNH_ALL; 30918c2ecf20Sopenharmony_ci arm_smmu_cmdq_issue_cmd(smmu, &cmd); 30928c2ecf20Sopenharmony_ci arm_smmu_cmdq_issue_sync(smmu); 30938c2ecf20Sopenharmony_ci 30948c2ecf20Sopenharmony_ci /* Event queue */ 30958c2ecf20Sopenharmony_ci writeq_relaxed(smmu->evtq.q.q_base, smmu->base + ARM_SMMU_EVTQ_BASE); 30968c2ecf20Sopenharmony_ci writel_relaxed(smmu->evtq.q.llq.prod, 30978c2ecf20Sopenharmony_ci arm_smmu_page1_fixup(ARM_SMMU_EVTQ_PROD, smmu)); 30988c2ecf20Sopenharmony_ci writel_relaxed(smmu->evtq.q.llq.cons, 30998c2ecf20Sopenharmony_ci arm_smmu_page1_fixup(ARM_SMMU_EVTQ_CONS, smmu)); 31008c2ecf20Sopenharmony_ci 31018c2ecf20Sopenharmony_ci enables |= CR0_EVTQEN; 31028c2ecf20Sopenharmony_ci ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0, 31038c2ecf20Sopenharmony_ci ARM_SMMU_CR0ACK); 31048c2ecf20Sopenharmony_ci if (ret) { 31058c2ecf20Sopenharmony_ci dev_err(smmu->dev, "failed to enable event queue\n"); 31068c2ecf20Sopenharmony_ci return ret; 31078c2ecf20Sopenharmony_ci } 31088c2ecf20Sopenharmony_ci 31098c2ecf20Sopenharmony_ci /* PRI queue */ 31108c2ecf20Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_PRI) { 31118c2ecf20Sopenharmony_ci writeq_relaxed(smmu->priq.q.q_base, 31128c2ecf20Sopenharmony_ci smmu->base + ARM_SMMU_PRIQ_BASE); 31138c2ecf20Sopenharmony_ci writel_relaxed(smmu->priq.q.llq.prod, 31148c2ecf20Sopenharmony_ci arm_smmu_page1_fixup(ARM_SMMU_PRIQ_PROD, smmu)); 31158c2ecf20Sopenharmony_ci writel_relaxed(smmu->priq.q.llq.cons, 31168c2ecf20Sopenharmony_ci arm_smmu_page1_fixup(ARM_SMMU_PRIQ_CONS, smmu)); 31178c2ecf20Sopenharmony_ci 31188c2ecf20Sopenharmony_ci enables |= CR0_PRIQEN; 31198c2ecf20Sopenharmony_ci ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0, 31208c2ecf20Sopenharmony_ci ARM_SMMU_CR0ACK); 31218c2ecf20Sopenharmony_ci if (ret) { 31228c2ecf20Sopenharmony_ci dev_err(smmu->dev, "failed to enable PRI queue\n"); 31238c2ecf20Sopenharmony_ci return ret; 31248c2ecf20Sopenharmony_ci } 31258c2ecf20Sopenharmony_ci } 31268c2ecf20Sopenharmony_ci 31278c2ecf20Sopenharmony_ci if (smmu->features & ARM_SMMU_FEAT_ATS) { 31288c2ecf20Sopenharmony_ci enables |= CR0_ATSCHK; 31298c2ecf20Sopenharmony_ci ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0, 31308c2ecf20Sopenharmony_ci ARM_SMMU_CR0ACK); 31318c2ecf20Sopenharmony_ci if (ret) { 31328c2ecf20Sopenharmony_ci dev_err(smmu->dev, "failed to enable ATS check\n"); 31338c2ecf20Sopenharmony_ci return ret; 31348c2ecf20Sopenharmony_ci } 31358c2ecf20Sopenharmony_ci } 31368c2ecf20Sopenharmony_ci 31378c2ecf20Sopenharmony_ci ret = arm_smmu_setup_irqs(smmu); 31388c2ecf20Sopenharmony_ci if (ret) { 31398c2ecf20Sopenharmony_ci dev_err(smmu->dev, "failed to setup irqs\n"); 31408c2ecf20Sopenharmony_ci return ret; 31418c2ecf20Sopenharmony_ci } 31428c2ecf20Sopenharmony_ci 31438c2ecf20Sopenharmony_ci if (is_kdump_kernel()) 31448c2ecf20Sopenharmony_ci enables &= ~(CR0_EVTQEN | CR0_PRIQEN); 31458c2ecf20Sopenharmony_ci 31468c2ecf20Sopenharmony_ci /* Enable the SMMU interface, or ensure bypass */ 31478c2ecf20Sopenharmony_ci if (!bypass || disable_bypass) { 31488c2ecf20Sopenharmony_ci enables |= CR0_SMMUEN; 31498c2ecf20Sopenharmony_ci } else { 31508c2ecf20Sopenharmony_ci ret = arm_smmu_update_gbpa(smmu, 0, GBPA_ABORT); 31518c2ecf20Sopenharmony_ci if (ret) 31528c2ecf20Sopenharmony_ci return ret; 31538c2ecf20Sopenharmony_ci } 31548c2ecf20Sopenharmony_ci ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0, 31558c2ecf20Sopenharmony_ci ARM_SMMU_CR0ACK); 31568c2ecf20Sopenharmony_ci if (ret) { 31578c2ecf20Sopenharmony_ci dev_err(smmu->dev, "failed to enable SMMU interface\n"); 31588c2ecf20Sopenharmony_ci return ret; 31598c2ecf20Sopenharmony_ci } 31608c2ecf20Sopenharmony_ci 31618c2ecf20Sopenharmony_ci return 0; 31628c2ecf20Sopenharmony_ci} 31638c2ecf20Sopenharmony_ci 31648c2ecf20Sopenharmony_cistatic int arm_smmu_device_hw_probe(struct arm_smmu_device *smmu) 31658c2ecf20Sopenharmony_ci{ 31668c2ecf20Sopenharmony_ci u32 reg; 31678c2ecf20Sopenharmony_ci bool coherent = smmu->features & ARM_SMMU_FEAT_COHERENCY; 31688c2ecf20Sopenharmony_ci 31698c2ecf20Sopenharmony_ci /* IDR0 */ 31708c2ecf20Sopenharmony_ci reg = readl_relaxed(smmu->base + ARM_SMMU_IDR0); 31718c2ecf20Sopenharmony_ci 31728c2ecf20Sopenharmony_ci /* 2-level structures */ 31738c2ecf20Sopenharmony_ci if (FIELD_GET(IDR0_ST_LVL, reg) == IDR0_ST_LVL_2LVL) 31748c2ecf20Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_2_LVL_STRTAB; 31758c2ecf20Sopenharmony_ci 31768c2ecf20Sopenharmony_ci if (reg & IDR0_CD2L) 31778c2ecf20Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_2_LVL_CDTAB; 31788c2ecf20Sopenharmony_ci 31798c2ecf20Sopenharmony_ci /* 31808c2ecf20Sopenharmony_ci * Translation table endianness. 31818c2ecf20Sopenharmony_ci * We currently require the same endianness as the CPU, but this 31828c2ecf20Sopenharmony_ci * could be changed later by adding a new IO_PGTABLE_QUIRK. 31838c2ecf20Sopenharmony_ci */ 31848c2ecf20Sopenharmony_ci switch (FIELD_GET(IDR0_TTENDIAN, reg)) { 31858c2ecf20Sopenharmony_ci case IDR0_TTENDIAN_MIXED: 31868c2ecf20Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_TT_LE | ARM_SMMU_FEAT_TT_BE; 31878c2ecf20Sopenharmony_ci break; 31888c2ecf20Sopenharmony_ci#ifdef __BIG_ENDIAN 31898c2ecf20Sopenharmony_ci case IDR0_TTENDIAN_BE: 31908c2ecf20Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_TT_BE; 31918c2ecf20Sopenharmony_ci break; 31928c2ecf20Sopenharmony_ci#else 31938c2ecf20Sopenharmony_ci case IDR0_TTENDIAN_LE: 31948c2ecf20Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_TT_LE; 31958c2ecf20Sopenharmony_ci break; 31968c2ecf20Sopenharmony_ci#endif 31978c2ecf20Sopenharmony_ci default: 31988c2ecf20Sopenharmony_ci dev_err(smmu->dev, "unknown/unsupported TT endianness!\n"); 31998c2ecf20Sopenharmony_ci return -ENXIO; 32008c2ecf20Sopenharmony_ci } 32018c2ecf20Sopenharmony_ci 32028c2ecf20Sopenharmony_ci /* Boolean feature flags */ 32038c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_PCI_PRI) && reg & IDR0_PRI) 32048c2ecf20Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_PRI; 32058c2ecf20Sopenharmony_ci 32068c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_PCI_ATS) && reg & IDR0_ATS) 32078c2ecf20Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_ATS; 32088c2ecf20Sopenharmony_ci 32098c2ecf20Sopenharmony_ci if (reg & IDR0_SEV) 32108c2ecf20Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_SEV; 32118c2ecf20Sopenharmony_ci 32128c2ecf20Sopenharmony_ci if (reg & IDR0_MSI) { 32138c2ecf20Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_MSI; 32148c2ecf20Sopenharmony_ci if (coherent && !disable_msipolling) 32158c2ecf20Sopenharmony_ci smmu->options |= ARM_SMMU_OPT_MSIPOLL; 32168c2ecf20Sopenharmony_ci } 32178c2ecf20Sopenharmony_ci 32188c2ecf20Sopenharmony_ci if (reg & IDR0_HYP) 32198c2ecf20Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_HYP; 32208c2ecf20Sopenharmony_ci 32218c2ecf20Sopenharmony_ci /* 32228c2ecf20Sopenharmony_ci * The coherency feature as set by FW is used in preference to the ID 32238c2ecf20Sopenharmony_ci * register, but warn on mismatch. 32248c2ecf20Sopenharmony_ci */ 32258c2ecf20Sopenharmony_ci if (!!(reg & IDR0_COHACC) != coherent) 32268c2ecf20Sopenharmony_ci dev_warn(smmu->dev, "IDR0.COHACC overridden by FW configuration (%s)\n", 32278c2ecf20Sopenharmony_ci coherent ? "true" : "false"); 32288c2ecf20Sopenharmony_ci 32298c2ecf20Sopenharmony_ci switch (FIELD_GET(IDR0_STALL_MODEL, reg)) { 32308c2ecf20Sopenharmony_ci case IDR0_STALL_MODEL_FORCE: 32318c2ecf20Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_STALL_FORCE; 32328c2ecf20Sopenharmony_ci fallthrough; 32338c2ecf20Sopenharmony_ci case IDR0_STALL_MODEL_STALL: 32348c2ecf20Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_STALLS; 32358c2ecf20Sopenharmony_ci } 32368c2ecf20Sopenharmony_ci 32378c2ecf20Sopenharmony_ci if (reg & IDR0_S1P) 32388c2ecf20Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_TRANS_S1; 32398c2ecf20Sopenharmony_ci 32408c2ecf20Sopenharmony_ci if (reg & IDR0_S2P) 32418c2ecf20Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_TRANS_S2; 32428c2ecf20Sopenharmony_ci 32438c2ecf20Sopenharmony_ci if (!(reg & (IDR0_S1P | IDR0_S2P))) { 32448c2ecf20Sopenharmony_ci dev_err(smmu->dev, "no translation support!\n"); 32458c2ecf20Sopenharmony_ci return -ENXIO; 32468c2ecf20Sopenharmony_ci } 32478c2ecf20Sopenharmony_ci 32488c2ecf20Sopenharmony_ci /* We only support the AArch64 table format at present */ 32498c2ecf20Sopenharmony_ci switch (FIELD_GET(IDR0_TTF, reg)) { 32508c2ecf20Sopenharmony_ci case IDR0_TTF_AARCH32_64: 32518c2ecf20Sopenharmony_ci smmu->ias = 40; 32528c2ecf20Sopenharmony_ci fallthrough; 32538c2ecf20Sopenharmony_ci case IDR0_TTF_AARCH64: 32548c2ecf20Sopenharmony_ci break; 32558c2ecf20Sopenharmony_ci default: 32568c2ecf20Sopenharmony_ci dev_err(smmu->dev, "AArch64 table format not supported!\n"); 32578c2ecf20Sopenharmony_ci return -ENXIO; 32588c2ecf20Sopenharmony_ci } 32598c2ecf20Sopenharmony_ci 32608c2ecf20Sopenharmony_ci /* ASID/VMID sizes */ 32618c2ecf20Sopenharmony_ci smmu->asid_bits = reg & IDR0_ASID16 ? 16 : 8; 32628c2ecf20Sopenharmony_ci smmu->vmid_bits = reg & IDR0_VMID16 ? 16 : 8; 32638c2ecf20Sopenharmony_ci 32648c2ecf20Sopenharmony_ci /* IDR1 */ 32658c2ecf20Sopenharmony_ci reg = readl_relaxed(smmu->base + ARM_SMMU_IDR1); 32668c2ecf20Sopenharmony_ci if (reg & (IDR1_TABLES_PRESET | IDR1_QUEUES_PRESET | IDR1_REL)) { 32678c2ecf20Sopenharmony_ci dev_err(smmu->dev, "embedded implementation not supported\n"); 32688c2ecf20Sopenharmony_ci return -ENXIO; 32698c2ecf20Sopenharmony_ci } 32708c2ecf20Sopenharmony_ci 32718c2ecf20Sopenharmony_ci /* Queue sizes, capped to ensure natural alignment */ 32728c2ecf20Sopenharmony_ci smmu->cmdq.q.llq.max_n_shift = min_t(u32, CMDQ_MAX_SZ_SHIFT, 32738c2ecf20Sopenharmony_ci FIELD_GET(IDR1_CMDQS, reg)); 32748c2ecf20Sopenharmony_ci if (smmu->cmdq.q.llq.max_n_shift <= ilog2(CMDQ_BATCH_ENTRIES)) { 32758c2ecf20Sopenharmony_ci /* 32768c2ecf20Sopenharmony_ci * We don't support splitting up batches, so one batch of 32778c2ecf20Sopenharmony_ci * commands plus an extra sync needs to fit inside the command 32788c2ecf20Sopenharmony_ci * queue. There's also no way we can handle the weird alignment 32798c2ecf20Sopenharmony_ci * restrictions on the base pointer for a unit-length queue. 32808c2ecf20Sopenharmony_ci */ 32818c2ecf20Sopenharmony_ci dev_err(smmu->dev, "command queue size <= %d entries not supported\n", 32828c2ecf20Sopenharmony_ci CMDQ_BATCH_ENTRIES); 32838c2ecf20Sopenharmony_ci return -ENXIO; 32848c2ecf20Sopenharmony_ci } 32858c2ecf20Sopenharmony_ci 32868c2ecf20Sopenharmony_ci smmu->evtq.q.llq.max_n_shift = min_t(u32, EVTQ_MAX_SZ_SHIFT, 32878c2ecf20Sopenharmony_ci FIELD_GET(IDR1_EVTQS, reg)); 32888c2ecf20Sopenharmony_ci smmu->priq.q.llq.max_n_shift = min_t(u32, PRIQ_MAX_SZ_SHIFT, 32898c2ecf20Sopenharmony_ci FIELD_GET(IDR1_PRIQS, reg)); 32908c2ecf20Sopenharmony_ci 32918c2ecf20Sopenharmony_ci /* SID/SSID sizes */ 32928c2ecf20Sopenharmony_ci smmu->ssid_bits = FIELD_GET(IDR1_SSIDSIZE, reg); 32938c2ecf20Sopenharmony_ci smmu->sid_bits = FIELD_GET(IDR1_SIDSIZE, reg); 32948c2ecf20Sopenharmony_ci 32958c2ecf20Sopenharmony_ci /* 32968c2ecf20Sopenharmony_ci * If the SMMU supports fewer bits than would fill a single L2 stream 32978c2ecf20Sopenharmony_ci * table, use a linear table instead. 32988c2ecf20Sopenharmony_ci */ 32998c2ecf20Sopenharmony_ci if (smmu->sid_bits <= STRTAB_SPLIT) 33008c2ecf20Sopenharmony_ci smmu->features &= ~ARM_SMMU_FEAT_2_LVL_STRTAB; 33018c2ecf20Sopenharmony_ci 33028c2ecf20Sopenharmony_ci /* IDR3 */ 33038c2ecf20Sopenharmony_ci reg = readl_relaxed(smmu->base + ARM_SMMU_IDR3); 33048c2ecf20Sopenharmony_ci if (FIELD_GET(IDR3_RIL, reg)) 33058c2ecf20Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_RANGE_INV; 33068c2ecf20Sopenharmony_ci 33078c2ecf20Sopenharmony_ci /* IDR5 */ 33088c2ecf20Sopenharmony_ci reg = readl_relaxed(smmu->base + ARM_SMMU_IDR5); 33098c2ecf20Sopenharmony_ci 33108c2ecf20Sopenharmony_ci /* Maximum number of outstanding stalls */ 33118c2ecf20Sopenharmony_ci smmu->evtq.max_stalls = FIELD_GET(IDR5_STALL_MAX, reg); 33128c2ecf20Sopenharmony_ci 33138c2ecf20Sopenharmony_ci /* Page sizes */ 33148c2ecf20Sopenharmony_ci if (reg & IDR5_GRAN64K) 33158c2ecf20Sopenharmony_ci smmu->pgsize_bitmap |= SZ_64K | SZ_512M; 33168c2ecf20Sopenharmony_ci if (reg & IDR5_GRAN16K) 33178c2ecf20Sopenharmony_ci smmu->pgsize_bitmap |= SZ_16K | SZ_32M; 33188c2ecf20Sopenharmony_ci if (reg & IDR5_GRAN4K) 33198c2ecf20Sopenharmony_ci smmu->pgsize_bitmap |= SZ_4K | SZ_2M | SZ_1G; 33208c2ecf20Sopenharmony_ci 33218c2ecf20Sopenharmony_ci /* Input address size */ 33228c2ecf20Sopenharmony_ci if (FIELD_GET(IDR5_VAX, reg) == IDR5_VAX_52_BIT) 33238c2ecf20Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_VAX; 33248c2ecf20Sopenharmony_ci 33258c2ecf20Sopenharmony_ci /* Output address size */ 33268c2ecf20Sopenharmony_ci switch (FIELD_GET(IDR5_OAS, reg)) { 33278c2ecf20Sopenharmony_ci case IDR5_OAS_32_BIT: 33288c2ecf20Sopenharmony_ci smmu->oas = 32; 33298c2ecf20Sopenharmony_ci break; 33308c2ecf20Sopenharmony_ci case IDR5_OAS_36_BIT: 33318c2ecf20Sopenharmony_ci smmu->oas = 36; 33328c2ecf20Sopenharmony_ci break; 33338c2ecf20Sopenharmony_ci case IDR5_OAS_40_BIT: 33348c2ecf20Sopenharmony_ci smmu->oas = 40; 33358c2ecf20Sopenharmony_ci break; 33368c2ecf20Sopenharmony_ci case IDR5_OAS_42_BIT: 33378c2ecf20Sopenharmony_ci smmu->oas = 42; 33388c2ecf20Sopenharmony_ci break; 33398c2ecf20Sopenharmony_ci case IDR5_OAS_44_BIT: 33408c2ecf20Sopenharmony_ci smmu->oas = 44; 33418c2ecf20Sopenharmony_ci break; 33428c2ecf20Sopenharmony_ci case IDR5_OAS_52_BIT: 33438c2ecf20Sopenharmony_ci smmu->oas = 52; 33448c2ecf20Sopenharmony_ci smmu->pgsize_bitmap |= 1ULL << 42; /* 4TB */ 33458c2ecf20Sopenharmony_ci break; 33468c2ecf20Sopenharmony_ci default: 33478c2ecf20Sopenharmony_ci dev_info(smmu->dev, 33488c2ecf20Sopenharmony_ci "unknown output address size. Truncating to 48-bit\n"); 33498c2ecf20Sopenharmony_ci fallthrough; 33508c2ecf20Sopenharmony_ci case IDR5_OAS_48_BIT: 33518c2ecf20Sopenharmony_ci smmu->oas = 48; 33528c2ecf20Sopenharmony_ci } 33538c2ecf20Sopenharmony_ci 33548c2ecf20Sopenharmony_ci if (arm_smmu_ops.pgsize_bitmap == -1UL) 33558c2ecf20Sopenharmony_ci arm_smmu_ops.pgsize_bitmap = smmu->pgsize_bitmap; 33568c2ecf20Sopenharmony_ci else 33578c2ecf20Sopenharmony_ci arm_smmu_ops.pgsize_bitmap |= smmu->pgsize_bitmap; 33588c2ecf20Sopenharmony_ci 33598c2ecf20Sopenharmony_ci /* Set the DMA mask for our table walker */ 33608c2ecf20Sopenharmony_ci if (dma_set_mask_and_coherent(smmu->dev, DMA_BIT_MASK(smmu->oas))) 33618c2ecf20Sopenharmony_ci dev_warn(smmu->dev, 33628c2ecf20Sopenharmony_ci "failed to set DMA mask for table walker\n"); 33638c2ecf20Sopenharmony_ci 33648c2ecf20Sopenharmony_ci smmu->ias = max(smmu->ias, smmu->oas); 33658c2ecf20Sopenharmony_ci 33668c2ecf20Sopenharmony_ci if (arm_smmu_sva_supported(smmu)) 33678c2ecf20Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_SVA; 33688c2ecf20Sopenharmony_ci 33698c2ecf20Sopenharmony_ci dev_info(smmu->dev, "ias %lu-bit, oas %lu-bit (features 0x%08x)\n", 33708c2ecf20Sopenharmony_ci smmu->ias, smmu->oas, smmu->features); 33718c2ecf20Sopenharmony_ci return 0; 33728c2ecf20Sopenharmony_ci} 33738c2ecf20Sopenharmony_ci 33748c2ecf20Sopenharmony_ci#ifdef CONFIG_ACPI 33758c2ecf20Sopenharmony_cistatic void acpi_smmu_get_options(u32 model, struct arm_smmu_device *smmu) 33768c2ecf20Sopenharmony_ci{ 33778c2ecf20Sopenharmony_ci switch (model) { 33788c2ecf20Sopenharmony_ci case ACPI_IORT_SMMU_V3_CAVIUM_CN99XX: 33798c2ecf20Sopenharmony_ci smmu->options |= ARM_SMMU_OPT_PAGE0_REGS_ONLY; 33808c2ecf20Sopenharmony_ci break; 33818c2ecf20Sopenharmony_ci case ACPI_IORT_SMMU_V3_HISILICON_HI161X: 33828c2ecf20Sopenharmony_ci smmu->options |= ARM_SMMU_OPT_SKIP_PREFETCH; 33838c2ecf20Sopenharmony_ci break; 33848c2ecf20Sopenharmony_ci } 33858c2ecf20Sopenharmony_ci 33868c2ecf20Sopenharmony_ci dev_notice(smmu->dev, "option mask 0x%x\n", smmu->options); 33878c2ecf20Sopenharmony_ci} 33888c2ecf20Sopenharmony_ci 33898c2ecf20Sopenharmony_cistatic int arm_smmu_device_acpi_probe(struct platform_device *pdev, 33908c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu) 33918c2ecf20Sopenharmony_ci{ 33928c2ecf20Sopenharmony_ci struct acpi_iort_smmu_v3 *iort_smmu; 33938c2ecf20Sopenharmony_ci struct device *dev = smmu->dev; 33948c2ecf20Sopenharmony_ci struct acpi_iort_node *node; 33958c2ecf20Sopenharmony_ci 33968c2ecf20Sopenharmony_ci node = *(struct acpi_iort_node **)dev_get_platdata(dev); 33978c2ecf20Sopenharmony_ci 33988c2ecf20Sopenharmony_ci /* Retrieve SMMUv3 specific data */ 33998c2ecf20Sopenharmony_ci iort_smmu = (struct acpi_iort_smmu_v3 *)node->node_data; 34008c2ecf20Sopenharmony_ci 34018c2ecf20Sopenharmony_ci acpi_smmu_get_options(iort_smmu->model, smmu); 34028c2ecf20Sopenharmony_ci 34038c2ecf20Sopenharmony_ci if (iort_smmu->flags & ACPI_IORT_SMMU_V3_COHACC_OVERRIDE) 34048c2ecf20Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_COHERENCY; 34058c2ecf20Sopenharmony_ci 34068c2ecf20Sopenharmony_ci return 0; 34078c2ecf20Sopenharmony_ci} 34088c2ecf20Sopenharmony_ci#else 34098c2ecf20Sopenharmony_cistatic inline int arm_smmu_device_acpi_probe(struct platform_device *pdev, 34108c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu) 34118c2ecf20Sopenharmony_ci{ 34128c2ecf20Sopenharmony_ci return -ENODEV; 34138c2ecf20Sopenharmony_ci} 34148c2ecf20Sopenharmony_ci#endif 34158c2ecf20Sopenharmony_ci 34168c2ecf20Sopenharmony_cistatic int arm_smmu_device_dt_probe(struct platform_device *pdev, 34178c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu) 34188c2ecf20Sopenharmony_ci{ 34198c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 34208c2ecf20Sopenharmony_ci u32 cells; 34218c2ecf20Sopenharmony_ci int ret = -EINVAL; 34228c2ecf20Sopenharmony_ci 34238c2ecf20Sopenharmony_ci if (of_property_read_u32(dev->of_node, "#iommu-cells", &cells)) 34248c2ecf20Sopenharmony_ci dev_err(dev, "missing #iommu-cells property\n"); 34258c2ecf20Sopenharmony_ci else if (cells != 1) 34268c2ecf20Sopenharmony_ci dev_err(dev, "invalid #iommu-cells value (%d)\n", cells); 34278c2ecf20Sopenharmony_ci else 34288c2ecf20Sopenharmony_ci ret = 0; 34298c2ecf20Sopenharmony_ci 34308c2ecf20Sopenharmony_ci parse_driver_options(smmu); 34318c2ecf20Sopenharmony_ci 34328c2ecf20Sopenharmony_ci if (of_dma_is_coherent(dev->of_node)) 34338c2ecf20Sopenharmony_ci smmu->features |= ARM_SMMU_FEAT_COHERENCY; 34348c2ecf20Sopenharmony_ci 34358c2ecf20Sopenharmony_ci return ret; 34368c2ecf20Sopenharmony_ci} 34378c2ecf20Sopenharmony_ci 34388c2ecf20Sopenharmony_cistatic unsigned long arm_smmu_resource_size(struct arm_smmu_device *smmu) 34398c2ecf20Sopenharmony_ci{ 34408c2ecf20Sopenharmony_ci if (smmu->options & ARM_SMMU_OPT_PAGE0_REGS_ONLY) 34418c2ecf20Sopenharmony_ci return SZ_64K; 34428c2ecf20Sopenharmony_ci else 34438c2ecf20Sopenharmony_ci return SZ_128K; 34448c2ecf20Sopenharmony_ci} 34458c2ecf20Sopenharmony_ci 34468c2ecf20Sopenharmony_cistatic int arm_smmu_set_bus_ops(struct iommu_ops *ops) 34478c2ecf20Sopenharmony_ci{ 34488c2ecf20Sopenharmony_ci int err; 34498c2ecf20Sopenharmony_ci 34508c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI 34518c2ecf20Sopenharmony_ci if (pci_bus_type.iommu_ops != ops) { 34528c2ecf20Sopenharmony_ci err = bus_set_iommu(&pci_bus_type, ops); 34538c2ecf20Sopenharmony_ci if (err) 34548c2ecf20Sopenharmony_ci return err; 34558c2ecf20Sopenharmony_ci } 34568c2ecf20Sopenharmony_ci#endif 34578c2ecf20Sopenharmony_ci#ifdef CONFIG_ARM_AMBA 34588c2ecf20Sopenharmony_ci if (amba_bustype.iommu_ops != ops) { 34598c2ecf20Sopenharmony_ci err = bus_set_iommu(&amba_bustype, ops); 34608c2ecf20Sopenharmony_ci if (err) 34618c2ecf20Sopenharmony_ci goto err_reset_pci_ops; 34628c2ecf20Sopenharmony_ci } 34638c2ecf20Sopenharmony_ci#endif 34648c2ecf20Sopenharmony_ci if (platform_bus_type.iommu_ops != ops) { 34658c2ecf20Sopenharmony_ci err = bus_set_iommu(&platform_bus_type, ops); 34668c2ecf20Sopenharmony_ci if (err) 34678c2ecf20Sopenharmony_ci goto err_reset_amba_ops; 34688c2ecf20Sopenharmony_ci } 34698c2ecf20Sopenharmony_ci 34708c2ecf20Sopenharmony_ci return 0; 34718c2ecf20Sopenharmony_ci 34728c2ecf20Sopenharmony_cierr_reset_amba_ops: 34738c2ecf20Sopenharmony_ci#ifdef CONFIG_ARM_AMBA 34748c2ecf20Sopenharmony_ci bus_set_iommu(&amba_bustype, NULL); 34758c2ecf20Sopenharmony_ci#endif 34768c2ecf20Sopenharmony_cierr_reset_pci_ops: __maybe_unused; 34778c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI 34788c2ecf20Sopenharmony_ci bus_set_iommu(&pci_bus_type, NULL); 34798c2ecf20Sopenharmony_ci#endif 34808c2ecf20Sopenharmony_ci return err; 34818c2ecf20Sopenharmony_ci} 34828c2ecf20Sopenharmony_ci 34838c2ecf20Sopenharmony_cistatic void __iomem *arm_smmu_ioremap(struct device *dev, resource_size_t start, 34848c2ecf20Sopenharmony_ci resource_size_t size) 34858c2ecf20Sopenharmony_ci{ 34868c2ecf20Sopenharmony_ci struct resource res = { 34878c2ecf20Sopenharmony_ci .flags = IORESOURCE_MEM, 34888c2ecf20Sopenharmony_ci .start = start, 34898c2ecf20Sopenharmony_ci .end = start + size - 1, 34908c2ecf20Sopenharmony_ci }; 34918c2ecf20Sopenharmony_ci 34928c2ecf20Sopenharmony_ci return devm_ioremap_resource(dev, &res); 34938c2ecf20Sopenharmony_ci} 34948c2ecf20Sopenharmony_ci 34958c2ecf20Sopenharmony_cistatic int arm_smmu_device_probe(struct platform_device *pdev) 34968c2ecf20Sopenharmony_ci{ 34978c2ecf20Sopenharmony_ci int irq, ret; 34988c2ecf20Sopenharmony_ci struct resource *res; 34998c2ecf20Sopenharmony_ci resource_size_t ioaddr; 35008c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu; 35018c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 35028c2ecf20Sopenharmony_ci bool bypass; 35038c2ecf20Sopenharmony_ci 35048c2ecf20Sopenharmony_ci smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL); 35058c2ecf20Sopenharmony_ci if (!smmu) { 35068c2ecf20Sopenharmony_ci dev_err(dev, "failed to allocate arm_smmu_device\n"); 35078c2ecf20Sopenharmony_ci return -ENOMEM; 35088c2ecf20Sopenharmony_ci } 35098c2ecf20Sopenharmony_ci smmu->dev = dev; 35108c2ecf20Sopenharmony_ci 35118c2ecf20Sopenharmony_ci if (dev->of_node) { 35128c2ecf20Sopenharmony_ci ret = arm_smmu_device_dt_probe(pdev, smmu); 35138c2ecf20Sopenharmony_ci } else { 35148c2ecf20Sopenharmony_ci ret = arm_smmu_device_acpi_probe(pdev, smmu); 35158c2ecf20Sopenharmony_ci if (ret == -ENODEV) 35168c2ecf20Sopenharmony_ci return ret; 35178c2ecf20Sopenharmony_ci } 35188c2ecf20Sopenharmony_ci 35198c2ecf20Sopenharmony_ci /* Set bypass mode according to firmware probing result */ 35208c2ecf20Sopenharmony_ci bypass = !!ret; 35218c2ecf20Sopenharmony_ci 35228c2ecf20Sopenharmony_ci /* Base address */ 35238c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 35248c2ecf20Sopenharmony_ci if (!res) 35258c2ecf20Sopenharmony_ci return -EINVAL; 35268c2ecf20Sopenharmony_ci if (resource_size(res) < arm_smmu_resource_size(smmu)) { 35278c2ecf20Sopenharmony_ci dev_err(dev, "MMIO region too small (%pr)\n", res); 35288c2ecf20Sopenharmony_ci return -EINVAL; 35298c2ecf20Sopenharmony_ci } 35308c2ecf20Sopenharmony_ci ioaddr = res->start; 35318c2ecf20Sopenharmony_ci 35328c2ecf20Sopenharmony_ci /* 35338c2ecf20Sopenharmony_ci * Don't map the IMPLEMENTATION DEFINED regions, since they may contain 35348c2ecf20Sopenharmony_ci * the PMCG registers which are reserved by the PMU driver. 35358c2ecf20Sopenharmony_ci */ 35368c2ecf20Sopenharmony_ci smmu->base = arm_smmu_ioremap(dev, ioaddr, ARM_SMMU_REG_SZ); 35378c2ecf20Sopenharmony_ci if (IS_ERR(smmu->base)) 35388c2ecf20Sopenharmony_ci return PTR_ERR(smmu->base); 35398c2ecf20Sopenharmony_ci 35408c2ecf20Sopenharmony_ci if (arm_smmu_resource_size(smmu) > SZ_64K) { 35418c2ecf20Sopenharmony_ci smmu->page1 = arm_smmu_ioremap(dev, ioaddr + SZ_64K, 35428c2ecf20Sopenharmony_ci ARM_SMMU_REG_SZ); 35438c2ecf20Sopenharmony_ci if (IS_ERR(smmu->page1)) 35448c2ecf20Sopenharmony_ci return PTR_ERR(smmu->page1); 35458c2ecf20Sopenharmony_ci } else { 35468c2ecf20Sopenharmony_ci smmu->page1 = smmu->base; 35478c2ecf20Sopenharmony_ci } 35488c2ecf20Sopenharmony_ci 35498c2ecf20Sopenharmony_ci /* Interrupt lines */ 35508c2ecf20Sopenharmony_ci 35518c2ecf20Sopenharmony_ci irq = platform_get_irq_byname_optional(pdev, "combined"); 35528c2ecf20Sopenharmony_ci if (irq > 0) 35538c2ecf20Sopenharmony_ci smmu->combined_irq = irq; 35548c2ecf20Sopenharmony_ci else { 35558c2ecf20Sopenharmony_ci irq = platform_get_irq_byname_optional(pdev, "eventq"); 35568c2ecf20Sopenharmony_ci if (irq > 0) 35578c2ecf20Sopenharmony_ci smmu->evtq.q.irq = irq; 35588c2ecf20Sopenharmony_ci 35598c2ecf20Sopenharmony_ci irq = platform_get_irq_byname_optional(pdev, "priq"); 35608c2ecf20Sopenharmony_ci if (irq > 0) 35618c2ecf20Sopenharmony_ci smmu->priq.q.irq = irq; 35628c2ecf20Sopenharmony_ci 35638c2ecf20Sopenharmony_ci irq = platform_get_irq_byname_optional(pdev, "gerror"); 35648c2ecf20Sopenharmony_ci if (irq > 0) 35658c2ecf20Sopenharmony_ci smmu->gerr_irq = irq; 35668c2ecf20Sopenharmony_ci } 35678c2ecf20Sopenharmony_ci /* Probe the h/w */ 35688c2ecf20Sopenharmony_ci ret = arm_smmu_device_hw_probe(smmu); 35698c2ecf20Sopenharmony_ci if (ret) 35708c2ecf20Sopenharmony_ci return ret; 35718c2ecf20Sopenharmony_ci 35728c2ecf20Sopenharmony_ci /* Initialise in-memory data structures */ 35738c2ecf20Sopenharmony_ci ret = arm_smmu_init_structures(smmu); 35748c2ecf20Sopenharmony_ci if (ret) 35758c2ecf20Sopenharmony_ci return ret; 35768c2ecf20Sopenharmony_ci 35778c2ecf20Sopenharmony_ci /* Record our private device structure */ 35788c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, smmu); 35798c2ecf20Sopenharmony_ci 35808c2ecf20Sopenharmony_ci /* Reset the device */ 35818c2ecf20Sopenharmony_ci ret = arm_smmu_device_reset(smmu, bypass); 35828c2ecf20Sopenharmony_ci if (ret) 35838c2ecf20Sopenharmony_ci return ret; 35848c2ecf20Sopenharmony_ci 35858c2ecf20Sopenharmony_ci /* And we're up. Go go go! */ 35868c2ecf20Sopenharmony_ci ret = iommu_device_sysfs_add(&smmu->iommu, dev, NULL, 35878c2ecf20Sopenharmony_ci "smmu3.%pa", &ioaddr); 35888c2ecf20Sopenharmony_ci if (ret) 35898c2ecf20Sopenharmony_ci return ret; 35908c2ecf20Sopenharmony_ci 35918c2ecf20Sopenharmony_ci iommu_device_set_ops(&smmu->iommu, &arm_smmu_ops); 35928c2ecf20Sopenharmony_ci iommu_device_set_fwnode(&smmu->iommu, dev->fwnode); 35938c2ecf20Sopenharmony_ci 35948c2ecf20Sopenharmony_ci ret = iommu_device_register(&smmu->iommu); 35958c2ecf20Sopenharmony_ci if (ret) { 35968c2ecf20Sopenharmony_ci dev_err(dev, "Failed to register iommu\n"); 35978c2ecf20Sopenharmony_ci return ret; 35988c2ecf20Sopenharmony_ci } 35998c2ecf20Sopenharmony_ci 36008c2ecf20Sopenharmony_ci return arm_smmu_set_bus_ops(&arm_smmu_ops); 36018c2ecf20Sopenharmony_ci} 36028c2ecf20Sopenharmony_ci 36038c2ecf20Sopenharmony_cistatic int arm_smmu_device_remove(struct platform_device *pdev) 36048c2ecf20Sopenharmony_ci{ 36058c2ecf20Sopenharmony_ci struct arm_smmu_device *smmu = platform_get_drvdata(pdev); 36068c2ecf20Sopenharmony_ci 36078c2ecf20Sopenharmony_ci arm_smmu_set_bus_ops(NULL); 36088c2ecf20Sopenharmony_ci iommu_device_unregister(&smmu->iommu); 36098c2ecf20Sopenharmony_ci iommu_device_sysfs_remove(&smmu->iommu); 36108c2ecf20Sopenharmony_ci arm_smmu_device_disable(smmu); 36118c2ecf20Sopenharmony_ci 36128c2ecf20Sopenharmony_ci return 0; 36138c2ecf20Sopenharmony_ci} 36148c2ecf20Sopenharmony_ci 36158c2ecf20Sopenharmony_cistatic void arm_smmu_device_shutdown(struct platform_device *pdev) 36168c2ecf20Sopenharmony_ci{ 36178c2ecf20Sopenharmony_ci arm_smmu_device_remove(pdev); 36188c2ecf20Sopenharmony_ci} 36198c2ecf20Sopenharmony_ci 36208c2ecf20Sopenharmony_cistatic const struct of_device_id arm_smmu_of_match[] = { 36218c2ecf20Sopenharmony_ci { .compatible = "arm,smmu-v3", }, 36228c2ecf20Sopenharmony_ci { }, 36238c2ecf20Sopenharmony_ci}; 36248c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, arm_smmu_of_match); 36258c2ecf20Sopenharmony_ci 36268c2ecf20Sopenharmony_cistatic struct platform_driver arm_smmu_driver = { 36278c2ecf20Sopenharmony_ci .driver = { 36288c2ecf20Sopenharmony_ci .name = "arm-smmu-v3", 36298c2ecf20Sopenharmony_ci .of_match_table = arm_smmu_of_match, 36308c2ecf20Sopenharmony_ci .suppress_bind_attrs = true, 36318c2ecf20Sopenharmony_ci }, 36328c2ecf20Sopenharmony_ci .probe = arm_smmu_device_probe, 36338c2ecf20Sopenharmony_ci .remove = arm_smmu_device_remove, 36348c2ecf20Sopenharmony_ci .shutdown = arm_smmu_device_shutdown, 36358c2ecf20Sopenharmony_ci}; 36368c2ecf20Sopenharmony_cimodule_platform_driver(arm_smmu_driver); 36378c2ecf20Sopenharmony_ci 36388c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("IOMMU API for ARM architected SMMUv3 implementations"); 36398c2ecf20Sopenharmony_ciMODULE_AUTHOR("Will Deacon <will@kernel.org>"); 36408c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:arm-smmu-v3"); 36418c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 3642