Arm SMMU updates for 6.8
- Device-tree binding updates: * Add additional compatible strings for Qualcomm SoCs * Document Adreno clocks for Qualcomm's SM8350 SoC - SMMUv2: * Implement support for the ->domain_alloc_paging() callback * Ensure Secure context is restored following suspend of Qualcomm SMMU implementation - SMMUv3: * Disable stalling mode for the "quiet" context descriptor * Minor refactoring and driver cleanups -----BEGIN PGP SIGNATURE----- iQFEBAABCgAuFiEEPxTL6PPUbjXGY88ct6xw3ITBYzQFAmV51JcQHHdpbGxAa2Vy bmVsLm9yZwAKCRC3rHDchMFjNMeSCACqYN4p5LyswEGOA7z/ZpyiLpW7Rbfw6mYj T+mLKgB9tLKw8Wp1nKM4MVylTxg2ykymUbRXahODbHC7au12u6ArXMm1jOzWGo7m ZoHyWzrSvUzQIFPXV49DaUcplOifWD/fHsp85k908SdM4QpaY9sn+ZWZqirXlpeo yJr0sOCR48bZeohBXRsZ+LFj0mDC8F1YwWdrND2cEtMEHqnSJasEJlUdujnROsCw V9pw47Nx6BPyp75HF38Tmy/g/WniwBITjBShb48rlUmyy2ddAPk4mSNXPvOdg+w9 HhBh/C/Ck6gzvahbe6OmESpevRPr3GF+rhzXe9r685PPssKLBN1o =j3OC -----END PGP SIGNATURE----- Merge tag 'arm-smmu-updates' of git://git.kernel.org/pub/scm/linux/kernel/git/will/linux into arm/smmu Arm SMMU updates for 6.8 - Device-tree binding updates: * Add additional compatible strings for Qualcomm SoCs * Document Adreno clocks for Qualcomm's SM8350 SoC - SMMUv2: * Implement support for the ->domain_alloc_paging() callback * Ensure Secure context is restored following suspend of Qualcomm SMMU implementation - SMMUv3: * Disable stalling mode for the "quiet" context descriptor * Minor refactoring and driver cleanups
This commit is contained in:
commit
3453c2b1d1
@ -56,6 +56,8 @@ properties:
|
||||
- qcom,sm8350-smmu-500
|
||||
- qcom,sm8450-smmu-500
|
||||
- qcom,sm8550-smmu-500
|
||||
- qcom,sm8650-smmu-500
|
||||
- qcom,x1e80100-smmu-500
|
||||
- const: qcom,smmu-500
|
||||
- const: arm,mmu-500
|
||||
|
||||
@ -89,6 +91,8 @@ properties:
|
||||
- qcom,sm8150-smmu-500
|
||||
- qcom,sm8250-smmu-500
|
||||
- qcom,sm8350-smmu-500
|
||||
- qcom,sm8450-smmu-500
|
||||
- qcom,sm8550-smmu-500
|
||||
- const: qcom,adreno-smmu
|
||||
- const: qcom,smmu-500
|
||||
- const: arm,mmu-500
|
||||
@ -429,6 +433,30 @@ allOf:
|
||||
- description: interface clock required to access smmu's registers
|
||||
through the TCU's programming interface.
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- qcom,sm8350-smmu-500
|
||||
- const: qcom,adreno-smmu
|
||||
- const: qcom,smmu-500
|
||||
- const: arm,mmu-500
|
||||
then:
|
||||
properties:
|
||||
clock-names:
|
||||
items:
|
||||
- const: bus
|
||||
- const: iface
|
||||
- const: ahb
|
||||
- const: hlos1_vote_gpu_smmu
|
||||
- const: cx_gmu
|
||||
- const: hub_cx_int
|
||||
- const: hub_aon
|
||||
clocks:
|
||||
minItems: 7
|
||||
maxItems: 7
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
@ -453,6 +481,50 @@ allOf:
|
||||
- description: Voter clock required for HLOS SMMU access
|
||||
- description: Interface clock required for register access
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
const: qcom,sm8450-smmu-500
|
||||
then:
|
||||
properties:
|
||||
clock-names:
|
||||
items:
|
||||
- const: gmu
|
||||
- const: hub
|
||||
- const: hlos
|
||||
- const: bus
|
||||
- const: iface
|
||||
- const: ahb
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: GMU clock
|
||||
- description: GPU HUB clock
|
||||
- description: HLOS vote clock
|
||||
- description: GPU memory bus clock
|
||||
- description: GPU SNoC bus clock
|
||||
- description: GPU AHB clock
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
const: qcom,sm8550-smmu-500
|
||||
then:
|
||||
properties:
|
||||
clock-names:
|
||||
items:
|
||||
- const: hlos
|
||||
- const: bus
|
||||
- const: iface
|
||||
- const: ahb
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: HLOS vote clock
|
||||
- description: GPU memory bus clock
|
||||
- description: GPU SNoC bus clock
|
||||
- description: GPU AHB clock
|
||||
|
||||
# Disallow clocks for all other platforms with specific compatibles
|
||||
- if:
|
||||
properties:
|
||||
@ -472,9 +544,8 @@ allOf:
|
||||
- qcom,sdx65-smmu-500
|
||||
- qcom,sm6350-smmu-500
|
||||
- qcom,sm6375-smmu-500
|
||||
- qcom,sm8350-smmu-500
|
||||
- qcom,sm8450-smmu-500
|
||||
- qcom,sm8550-smmu-500
|
||||
- qcom,sm8650-smmu-500
|
||||
- qcom,x1e80100-smmu-500
|
||||
then:
|
||||
properties:
|
||||
clock-names: false
|
||||
|
@ -1063,6 +1063,7 @@ int arm_smmu_write_ctx_desc(struct arm_smmu_master *master, int ssid,
|
||||
bool cd_live;
|
||||
__le64 *cdptr;
|
||||
struct arm_smmu_ctx_desc_cfg *cd_table = &master->cd_table;
|
||||
struct arm_smmu_device *smmu = master->smmu;
|
||||
|
||||
if (WARN_ON(ssid >= (1 << cd_table->s1cdmax)))
|
||||
return -E2BIG;
|
||||
@ -1077,6 +1078,8 @@ int arm_smmu_write_ctx_desc(struct arm_smmu_master *master, int ssid,
|
||||
if (!cd) { /* (5) */
|
||||
val = 0;
|
||||
} else if (cd == &quiet_cd) { /* (4) */
|
||||
if (!(smmu->features & ARM_SMMU_FEAT_STALL_FORCE))
|
||||
val &= ~(CTXDESC_CD_0_S | CTXDESC_CD_0_R);
|
||||
val |= CTXDESC_CD_0_TCR_EPD0;
|
||||
} else if (cd_live) { /* (3) */
|
||||
val &= ~CTXDESC_CD_0_ASID;
|
||||
@ -1249,7 +1252,7 @@ static void arm_smmu_sync_ste_for_sid(struct arm_smmu_device *smmu, u32 sid)
|
||||
}
|
||||
|
||||
static void arm_smmu_write_strtab_ent(struct arm_smmu_master *master, u32 sid,
|
||||
__le64 *dst)
|
||||
struct arm_smmu_ste *dst)
|
||||
{
|
||||
/*
|
||||
* This is hideously complicated, but we only really care about
|
||||
@ -1267,12 +1270,12 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_master *master, u32 sid,
|
||||
* 2. Write everything apart from dword 0, sync, write dword 0, sync
|
||||
* 3. Update Config, sync
|
||||
*/
|
||||
u64 val = le64_to_cpu(dst[0]);
|
||||
u64 val = le64_to_cpu(dst->data[0]);
|
||||
bool ste_live = false;
|
||||
struct arm_smmu_device *smmu = NULL;
|
||||
struct arm_smmu_device *smmu = master->smmu;
|
||||
struct arm_smmu_ctx_desc_cfg *cd_table = NULL;
|
||||
struct arm_smmu_s2_cfg *s2_cfg = NULL;
|
||||
struct arm_smmu_domain *smmu_domain = NULL;
|
||||
struct arm_smmu_domain *smmu_domain = master->domain;
|
||||
struct arm_smmu_cmdq_ent prefetch_cmd = {
|
||||
.opcode = CMDQ_OP_PREFETCH_CFG,
|
||||
.prefetch = {
|
||||
@ -1280,18 +1283,12 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_master *master, u32 sid,
|
||||
},
|
||||
};
|
||||
|
||||
if (master) {
|
||||
smmu_domain = master->domain;
|
||||
smmu = master->smmu;
|
||||
}
|
||||
|
||||
if (smmu_domain) {
|
||||
switch (smmu_domain->stage) {
|
||||
case ARM_SMMU_DOMAIN_S1:
|
||||
cd_table = &master->cd_table;
|
||||
break;
|
||||
case ARM_SMMU_DOMAIN_S2:
|
||||
case ARM_SMMU_DOMAIN_NESTED:
|
||||
s2_cfg = &smmu_domain->s2_cfg;
|
||||
break;
|
||||
default:
|
||||
@ -1325,10 +1322,10 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_master *master, u32 sid,
|
||||
else
|
||||
val |= FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_BYPASS);
|
||||
|
||||
dst[0] = cpu_to_le64(val);
|
||||
dst[1] = cpu_to_le64(FIELD_PREP(STRTAB_STE_1_SHCFG,
|
||||
dst->data[0] = cpu_to_le64(val);
|
||||
dst->data[1] = cpu_to_le64(FIELD_PREP(STRTAB_STE_1_SHCFG,
|
||||
STRTAB_STE_1_SHCFG_INCOMING));
|
||||
dst[2] = 0; /* Nuke the VMID */
|
||||
dst->data[2] = 0; /* Nuke the VMID */
|
||||
/*
|
||||
* The SMMU can perform negative caching, so we must sync
|
||||
* the STE regardless of whether the old value was live.
|
||||
@ -1343,7 +1340,7 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_master *master, u32 sid,
|
||||
STRTAB_STE_1_STRW_EL2 : STRTAB_STE_1_STRW_NSEL1;
|
||||
|
||||
BUG_ON(ste_live);
|
||||
dst[1] = cpu_to_le64(
|
||||
dst->data[1] = cpu_to_le64(
|
||||
FIELD_PREP(STRTAB_STE_1_S1DSS, STRTAB_STE_1_S1DSS_SSID0) |
|
||||
FIELD_PREP(STRTAB_STE_1_S1CIR, STRTAB_STE_1_S1C_CACHE_WBRA) |
|
||||
FIELD_PREP(STRTAB_STE_1_S1COR, STRTAB_STE_1_S1C_CACHE_WBRA) |
|
||||
@ -1352,7 +1349,7 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_master *master, u32 sid,
|
||||
|
||||
if (smmu->features & ARM_SMMU_FEAT_STALLS &&
|
||||
!master->stall_enabled)
|
||||
dst[1] |= cpu_to_le64(STRTAB_STE_1_S1STALLD);
|
||||
dst->data[1] |= cpu_to_le64(STRTAB_STE_1_S1STALLD);
|
||||
|
||||
val |= (cd_table->cdtab_dma & STRTAB_STE_0_S1CTXPTR_MASK) |
|
||||
FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_S1_TRANS) |
|
||||
@ -1362,7 +1359,7 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_master *master, u32 sid,
|
||||
|
||||
if (s2_cfg) {
|
||||
BUG_ON(ste_live);
|
||||
dst[2] = cpu_to_le64(
|
||||
dst->data[2] = cpu_to_le64(
|
||||
FIELD_PREP(STRTAB_STE_2_S2VMID, s2_cfg->vmid) |
|
||||
FIELD_PREP(STRTAB_STE_2_VTCR, s2_cfg->vtcr) |
|
||||
#ifdef __BIG_ENDIAN
|
||||
@ -1371,18 +1368,18 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_master *master, u32 sid,
|
||||
STRTAB_STE_2_S2PTW | STRTAB_STE_2_S2AA64 |
|
||||
STRTAB_STE_2_S2R);
|
||||
|
||||
dst[3] = cpu_to_le64(s2_cfg->vttbr & STRTAB_STE_3_S2TTB_MASK);
|
||||
dst->data[3] = cpu_to_le64(s2_cfg->vttbr & STRTAB_STE_3_S2TTB_MASK);
|
||||
|
||||
val |= FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_S2_TRANS);
|
||||
}
|
||||
|
||||
if (master->ats_enabled)
|
||||
dst[1] |= cpu_to_le64(FIELD_PREP(STRTAB_STE_1_EATS,
|
||||
dst->data[1] |= cpu_to_le64(FIELD_PREP(STRTAB_STE_1_EATS,
|
||||
STRTAB_STE_1_EATS_TRANS));
|
||||
|
||||
arm_smmu_sync_ste_for_sid(smmu, sid);
|
||||
/* See comment in arm_smmu_write_ctx_desc() */
|
||||
WRITE_ONCE(dst[0], cpu_to_le64(val));
|
||||
WRITE_ONCE(dst->data[0], cpu_to_le64(val));
|
||||
arm_smmu_sync_ste_for_sid(smmu, sid);
|
||||
|
||||
/* It's likely that we'll want to use the new STE soon */
|
||||
@ -1390,7 +1387,8 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_master *master, u32 sid,
|
||||
arm_smmu_cmdq_issue_cmd(smmu, &prefetch_cmd);
|
||||
}
|
||||
|
||||
static void arm_smmu_init_bypass_stes(__le64 *strtab, unsigned int nent, bool force)
|
||||
static void arm_smmu_init_bypass_stes(struct arm_smmu_ste *strtab,
|
||||
unsigned int nent, bool force)
|
||||
{
|
||||
unsigned int i;
|
||||
u64 val = STRTAB_STE_0_V;
|
||||
@ -1401,11 +1399,11 @@ static void arm_smmu_init_bypass_stes(__le64 *strtab, unsigned int nent, bool fo
|
||||
val |= FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_BYPASS);
|
||||
|
||||
for (i = 0; i < nent; ++i) {
|
||||
strtab[0] = cpu_to_le64(val);
|
||||
strtab[1] = cpu_to_le64(FIELD_PREP(STRTAB_STE_1_SHCFG,
|
||||
STRTAB_STE_1_SHCFG_INCOMING));
|
||||
strtab[2] = 0;
|
||||
strtab += STRTAB_STE_DWORDS;
|
||||
strtab->data[0] = cpu_to_le64(val);
|
||||
strtab->data[1] = cpu_to_le64(FIELD_PREP(
|
||||
STRTAB_STE_1_SHCFG, STRTAB_STE_1_SHCFG_INCOMING));
|
||||
strtab->data[2] = 0;
|
||||
strtab++;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2171,7 +2169,6 @@ static int arm_smmu_domain_finalise(struct iommu_domain *domain)
|
||||
fmt = ARM_64_LPAE_S1;
|
||||
finalise_stage_fn = arm_smmu_domain_finalise_s1;
|
||||
break;
|
||||
case ARM_SMMU_DOMAIN_NESTED:
|
||||
case ARM_SMMU_DOMAIN_S2:
|
||||
ias = smmu->ias;
|
||||
oas = smmu->oas;
|
||||
@ -2209,26 +2206,23 @@ static int arm_smmu_domain_finalise(struct iommu_domain *domain)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __le64 *arm_smmu_get_step_for_sid(struct arm_smmu_device *smmu, u32 sid)
|
||||
static struct arm_smmu_ste *
|
||||
arm_smmu_get_step_for_sid(struct arm_smmu_device *smmu, u32 sid)
|
||||
{
|
||||
__le64 *step;
|
||||
struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;
|
||||
|
||||
if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB) {
|
||||
struct arm_smmu_strtab_l1_desc *l1_desc;
|
||||
int idx;
|
||||
unsigned int idx1, idx2;
|
||||
|
||||
/* Two-level walk */
|
||||
idx = (sid >> STRTAB_SPLIT) * STRTAB_L1_DESC_DWORDS;
|
||||
l1_desc = &cfg->l1_desc[idx];
|
||||
idx = (sid & ((1 << STRTAB_SPLIT) - 1)) * STRTAB_STE_DWORDS;
|
||||
step = &l1_desc->l2ptr[idx];
|
||||
idx1 = (sid >> STRTAB_SPLIT) * STRTAB_L1_DESC_DWORDS;
|
||||
idx2 = sid & ((1 << STRTAB_SPLIT) - 1);
|
||||
return &cfg->l1_desc[idx1].l2ptr[idx2];
|
||||
} else {
|
||||
/* Simple linear lookup */
|
||||
step = &cfg->strtab[sid * STRTAB_STE_DWORDS];
|
||||
return (struct arm_smmu_ste *)&cfg
|
||||
->strtab[sid * STRTAB_STE_DWORDS];
|
||||
}
|
||||
|
||||
return step;
|
||||
}
|
||||
|
||||
static void arm_smmu_install_ste_for_dev(struct arm_smmu_master *master)
|
||||
@ -2238,7 +2232,8 @@ static void arm_smmu_install_ste_for_dev(struct arm_smmu_master *master)
|
||||
|
||||
for (i = 0; i < master->num_streams; ++i) {
|
||||
u32 sid = master->streams[i].id;
|
||||
__le64 *step = arm_smmu_get_step_for_sid(smmu, sid);
|
||||
struct arm_smmu_ste *step =
|
||||
arm_smmu_get_step_for_sid(smmu, sid);
|
||||
|
||||
/* Bridged PCI devices may end up with duplicated IDs */
|
||||
for (j = 0; j < i; j++)
|
||||
@ -2742,7 +2737,7 @@ static int arm_smmu_enable_nesting(struct iommu_domain *domain)
|
||||
if (smmu_domain->smmu)
|
||||
ret = -EPERM;
|
||||
else
|
||||
smmu_domain->stage = ARM_SMMU_DOMAIN_NESTED;
|
||||
smmu_domain->stage = ARM_SMMU_DOMAIN_S2;
|
||||
mutex_unlock(&smmu_domain->init_mutex);
|
||||
|
||||
return ret;
|
||||
@ -3769,7 +3764,7 @@ static void arm_smmu_rmr_install_bypass_ste(struct arm_smmu_device *smmu)
|
||||
iort_get_rmr_sids(dev_fwnode(smmu->dev), &rmr_list);
|
||||
|
||||
list_for_each_entry(e, &rmr_list, list) {
|
||||
__le64 *step;
|
||||
struct arm_smmu_ste *step;
|
||||
struct iommu_iort_rmr_data *rmr;
|
||||
int ret, i;
|
||||
|
||||
|
@ -206,6 +206,11 @@
|
||||
#define STRTAB_L1_DESC_L2PTR_MASK GENMASK_ULL(51, 6)
|
||||
|
||||
#define STRTAB_STE_DWORDS 8
|
||||
|
||||
struct arm_smmu_ste {
|
||||
__le64 data[STRTAB_STE_DWORDS];
|
||||
};
|
||||
|
||||
#define STRTAB_STE_0_V (1UL << 0)
|
||||
#define STRTAB_STE_0_CFG GENMASK_ULL(3, 1)
|
||||
#define STRTAB_STE_0_CFG_ABORT 0
|
||||
@ -571,7 +576,7 @@ struct arm_smmu_priq {
|
||||
struct arm_smmu_strtab_l1_desc {
|
||||
u8 span;
|
||||
|
||||
__le64 *l2ptr;
|
||||
struct arm_smmu_ste *l2ptr;
|
||||
dma_addr_t l2ptr_dma;
|
||||
};
|
||||
|
||||
@ -710,7 +715,6 @@ struct arm_smmu_master {
|
||||
enum arm_smmu_domain_stage {
|
||||
ARM_SMMU_DOMAIN_S1 = 0,
|
||||
ARM_SMMU_DOMAIN_S2,
|
||||
ARM_SMMU_DOMAIN_NESTED,
|
||||
ARM_SMMU_DOMAIN_BYPASS,
|
||||
};
|
||||
|
||||
|
@ -243,8 +243,10 @@ static int qcom_adreno_smmu_init_context(struct arm_smmu_domain *smmu_domain,
|
||||
|
||||
static const struct of_device_id qcom_smmu_client_of_match[] __maybe_unused = {
|
||||
{ .compatible = "qcom,adreno" },
|
||||
{ .compatible = "qcom,adreno-gmu" },
|
||||
{ .compatible = "qcom,mdp4" },
|
||||
{ .compatible = "qcom,mdss" },
|
||||
{ .compatible = "qcom,qcm2290-mdss" },
|
||||
{ .compatible = "qcom,sc7180-mdss" },
|
||||
{ .compatible = "qcom,sc7180-mss-pil" },
|
||||
{ .compatible = "qcom,sc7280-mdss" },
|
||||
|
@ -82,6 +82,23 @@ static inline void arm_smmu_rpm_put(struct arm_smmu_device *smmu)
|
||||
pm_runtime_put_autosuspend(smmu->dev);
|
||||
}
|
||||
|
||||
static void arm_smmu_rpm_use_autosuspend(struct arm_smmu_device *smmu)
|
||||
{
|
||||
/*
|
||||
* Setup an autosuspend delay to avoid bouncing runpm state.
|
||||
* Otherwise, if a driver for a suspended consumer device
|
||||
* unmaps buffers, it will runpm resume/suspend for each one.
|
||||
*
|
||||
* For example, when used by a GPU device, when an application
|
||||
* or game exits, it can trigger unmapping 100s or 1000s of
|
||||
* buffers. With a runpm cycle for each buffer, that adds up
|
||||
* to 5-10sec worth of reprogramming the context bank, while
|
||||
* the system appears to be locked up to the user.
|
||||
*/
|
||||
pm_runtime_set_autosuspend_delay(smmu->dev, 20);
|
||||
pm_runtime_use_autosuspend(smmu->dev);
|
||||
}
|
||||
|
||||
static struct arm_smmu_domain *to_smmu_domain(struct iommu_domain *dom)
|
||||
{
|
||||
return container_of(dom, struct arm_smmu_domain, domain);
|
||||
@ -392,8 +409,7 @@ static irqreturn_t arm_smmu_context_fault(int irq, void *dev)
|
||||
{
|
||||
u32 fsr, fsynr, cbfrsynra;
|
||||
unsigned long iova;
|
||||
struct iommu_domain *domain = dev;
|
||||
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
|
||||
struct arm_smmu_domain *smmu_domain = dev;
|
||||
struct arm_smmu_device *smmu = smmu_domain->smmu;
|
||||
int idx = smmu_domain->cfg.cbndx;
|
||||
int ret;
|
||||
@ -406,7 +422,7 @@ static irqreturn_t arm_smmu_context_fault(int irq, void *dev)
|
||||
iova = arm_smmu_cb_readq(smmu, idx, ARM_SMMU_CB_FAR);
|
||||
cbfrsynra = arm_smmu_gr1_read(smmu, ARM_SMMU_GR1_CBFRSYNRA(idx));
|
||||
|
||||
ret = report_iommu_fault(domain, NULL, iova,
|
||||
ret = report_iommu_fault(&smmu_domain->domain, NULL, iova,
|
||||
fsynr & ARM_SMMU_FSYNR0_WNR ? IOMMU_FAULT_WRITE : IOMMU_FAULT_READ);
|
||||
|
||||
if (ret == -ENOSYS)
|
||||
@ -607,7 +623,7 @@ static int arm_smmu_alloc_context_bank(struct arm_smmu_domain *smmu_domain,
|
||||
return __arm_smmu_alloc_bitmap(smmu->context_map, start, smmu->num_context_banks);
|
||||
}
|
||||
|
||||
static int arm_smmu_init_domain_context(struct iommu_domain *domain,
|
||||
static int arm_smmu_init_domain_context(struct arm_smmu_domain *smmu_domain,
|
||||
struct arm_smmu_device *smmu,
|
||||
struct device *dev)
|
||||
{
|
||||
@ -616,7 +632,7 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
|
||||
struct io_pgtable_ops *pgtbl_ops;
|
||||
struct io_pgtable_cfg pgtbl_cfg;
|
||||
enum io_pgtable_fmt fmt;
|
||||
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
|
||||
struct iommu_domain *domain = &smmu_domain->domain;
|
||||
struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
|
||||
irqreturn_t (*context_fault)(int irq, void *dev);
|
||||
|
||||
@ -624,12 +640,6 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
|
||||
if (smmu_domain->smmu)
|
||||
goto out_unlock;
|
||||
|
||||
if (domain->type == IOMMU_DOMAIN_IDENTITY) {
|
||||
smmu_domain->stage = ARM_SMMU_DOMAIN_BYPASS;
|
||||
smmu_domain->smmu = smmu;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
/*
|
||||
* Mapping the requested stage onto what we support is surprisingly
|
||||
* complicated, mainly because the spec allows S1+S2 SMMUs without
|
||||
@ -796,8 +806,8 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
|
||||
else
|
||||
context_fault = arm_smmu_context_fault;
|
||||
|
||||
ret = devm_request_irq(smmu->dev, irq, context_fault,
|
||||
IRQF_SHARED, "arm-smmu-context-fault", domain);
|
||||
ret = devm_request_irq(smmu->dev, irq, context_fault, IRQF_SHARED,
|
||||
"arm-smmu-context-fault", smmu_domain);
|
||||
if (ret < 0) {
|
||||
dev_err(smmu->dev, "failed to request context IRQ %d (%u)\n",
|
||||
cfg->irptndx, irq);
|
||||
@ -818,14 +828,13 @@ out_unlock:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void arm_smmu_destroy_domain_context(struct iommu_domain *domain)
|
||||
static void arm_smmu_destroy_domain_context(struct arm_smmu_domain *smmu_domain)
|
||||
{
|
||||
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
|
||||
struct arm_smmu_device *smmu = smmu_domain->smmu;
|
||||
struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
|
||||
int ret, irq;
|
||||
|
||||
if (!smmu || domain->type == IOMMU_DOMAIN_IDENTITY)
|
||||
if (!smmu)
|
||||
return;
|
||||
|
||||
ret = arm_smmu_rpm_get(smmu);
|
||||
@ -841,7 +850,7 @@ static void arm_smmu_destroy_domain_context(struct iommu_domain *domain)
|
||||
|
||||
if (cfg->irptndx != ARM_SMMU_INVALID_IRPTNDX) {
|
||||
irq = smmu->irqs[cfg->irptndx];
|
||||
devm_free_irq(smmu->dev, irq, domain);
|
||||
devm_free_irq(smmu->dev, irq, smmu_domain);
|
||||
}
|
||||
|
||||
free_io_pgtable_ops(smmu_domain->pgtbl_ops);
|
||||
@ -850,14 +859,10 @@ static void arm_smmu_destroy_domain_context(struct iommu_domain *domain)
|
||||
arm_smmu_rpm_put(smmu);
|
||||
}
|
||||
|
||||
static struct iommu_domain *arm_smmu_domain_alloc(unsigned type)
|
||||
static struct iommu_domain *arm_smmu_domain_alloc_paging(struct device *dev)
|
||||
{
|
||||
struct arm_smmu_domain *smmu_domain;
|
||||
|
||||
if (type != IOMMU_DOMAIN_UNMANAGED && type != IOMMU_DOMAIN_IDENTITY) {
|
||||
if (using_legacy_binding || type != IOMMU_DOMAIN_DMA)
|
||||
return NULL;
|
||||
}
|
||||
/*
|
||||
* Allocate the domain and initialise some of its data structures.
|
||||
* We can't really do anything meaningful until we've added a
|
||||
@ -870,6 +875,15 @@ static struct iommu_domain *arm_smmu_domain_alloc(unsigned type)
|
||||
mutex_init(&smmu_domain->init_mutex);
|
||||
spin_lock_init(&smmu_domain->cb_lock);
|
||||
|
||||
if (dev) {
|
||||
struct arm_smmu_master_cfg *cfg = dev_iommu_priv_get(dev);
|
||||
|
||||
if (arm_smmu_init_domain_context(smmu_domain, cfg->smmu, dev)) {
|
||||
kfree(smmu_domain);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return &smmu_domain->domain;
|
||||
}
|
||||
|
||||
@ -881,7 +895,7 @@ static void arm_smmu_domain_free(struct iommu_domain *domain)
|
||||
* Free the domain resources. We assume that all devices have
|
||||
* already been detached.
|
||||
*/
|
||||
arm_smmu_destroy_domain_context(domain);
|
||||
arm_smmu_destroy_domain_context(smmu_domain);
|
||||
kfree(smmu_domain);
|
||||
}
|
||||
|
||||
@ -1081,21 +1095,14 @@ static void arm_smmu_master_free_smes(struct arm_smmu_master_cfg *cfg,
|
||||
mutex_unlock(&smmu->stream_map_mutex);
|
||||
}
|
||||
|
||||
static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
|
||||
struct arm_smmu_master_cfg *cfg,
|
||||
struct iommu_fwspec *fwspec)
|
||||
static void arm_smmu_master_install_s2crs(struct arm_smmu_master_cfg *cfg,
|
||||
enum arm_smmu_s2cr_type type,
|
||||
u8 cbndx, struct iommu_fwspec *fwspec)
|
||||
{
|
||||
struct arm_smmu_device *smmu = smmu_domain->smmu;
|
||||
struct arm_smmu_device *smmu = cfg->smmu;
|
||||
struct arm_smmu_s2cr *s2cr = smmu->s2crs;
|
||||
u8 cbndx = smmu_domain->cfg.cbndx;
|
||||
enum arm_smmu_s2cr_type type;
|
||||
int i, idx;
|
||||
|
||||
if (smmu_domain->stage == ARM_SMMU_DOMAIN_BYPASS)
|
||||
type = S2CR_TYPE_BYPASS;
|
||||
else
|
||||
type = S2CR_TYPE_TRANS;
|
||||
|
||||
for_each_cfg_sme(cfg, fwspec, i, idx) {
|
||||
if (type == s2cr[idx].type && cbndx == s2cr[idx].cbndx)
|
||||
continue;
|
||||
@ -1105,7 +1112,6 @@ static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
|
||||
s2cr[idx].cbndx = cbndx;
|
||||
arm_smmu_write_s2cr(smmu, idx);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
|
||||
@ -1139,7 +1145,7 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
|
||||
return ret;
|
||||
|
||||
/* Ensure that the domain is finalised */
|
||||
ret = arm_smmu_init_domain_context(domain, smmu, dev);
|
||||
ret = arm_smmu_init_domain_context(smmu_domain, smmu, dev);
|
||||
if (ret < 0)
|
||||
goto rpm_put;
|
||||
|
||||
@ -1153,27 +1159,66 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
|
||||
}
|
||||
|
||||
/* Looks ok, so add the device to the domain */
|
||||
ret = arm_smmu_domain_add_master(smmu_domain, cfg, fwspec);
|
||||
|
||||
/*
|
||||
* Setup an autosuspend delay to avoid bouncing runpm state.
|
||||
* Otherwise, if a driver for a suspended consumer device
|
||||
* unmaps buffers, it will runpm resume/suspend for each one.
|
||||
*
|
||||
* For example, when used by a GPU device, when an application
|
||||
* or game exits, it can trigger unmapping 100s or 1000s of
|
||||
* buffers. With a runpm cycle for each buffer, that adds up
|
||||
* to 5-10sec worth of reprogramming the context bank, while
|
||||
* the system appears to be locked up to the user.
|
||||
*/
|
||||
pm_runtime_set_autosuspend_delay(smmu->dev, 20);
|
||||
pm_runtime_use_autosuspend(smmu->dev);
|
||||
|
||||
arm_smmu_master_install_s2crs(cfg, S2CR_TYPE_TRANS,
|
||||
smmu_domain->cfg.cbndx, fwspec);
|
||||
arm_smmu_rpm_use_autosuspend(smmu);
|
||||
rpm_put:
|
||||
arm_smmu_rpm_put(smmu);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int arm_smmu_attach_dev_type(struct device *dev,
|
||||
enum arm_smmu_s2cr_type type)
|
||||
{
|
||||
struct arm_smmu_master_cfg *cfg = dev_iommu_priv_get(dev);
|
||||
struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
|
||||
struct arm_smmu_device *smmu;
|
||||
int ret;
|
||||
|
||||
if (!cfg)
|
||||
return -ENODEV;
|
||||
smmu = cfg->smmu;
|
||||
|
||||
ret = arm_smmu_rpm_get(smmu);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
arm_smmu_master_install_s2crs(cfg, type, 0, fwspec);
|
||||
arm_smmu_rpm_use_autosuspend(smmu);
|
||||
arm_smmu_rpm_put(smmu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int arm_smmu_attach_dev_identity(struct iommu_domain *domain,
|
||||
struct device *dev)
|
||||
{
|
||||
return arm_smmu_attach_dev_type(dev, S2CR_TYPE_BYPASS);
|
||||
}
|
||||
|
||||
static const struct iommu_domain_ops arm_smmu_identity_ops = {
|
||||
.attach_dev = arm_smmu_attach_dev_identity,
|
||||
};
|
||||
|
||||
static struct iommu_domain arm_smmu_identity_domain = {
|
||||
.type = IOMMU_DOMAIN_IDENTITY,
|
||||
.ops = &arm_smmu_identity_ops,
|
||||
};
|
||||
|
||||
static int arm_smmu_attach_dev_blocked(struct iommu_domain *domain,
|
||||
struct device *dev)
|
||||
{
|
||||
return arm_smmu_attach_dev_type(dev, S2CR_TYPE_FAULT);
|
||||
}
|
||||
|
||||
static const struct iommu_domain_ops arm_smmu_blocked_ops = {
|
||||
.attach_dev = arm_smmu_attach_dev_blocked,
|
||||
};
|
||||
|
||||
static struct iommu_domain arm_smmu_blocked_domain = {
|
||||
.type = IOMMU_DOMAIN_BLOCKED,
|
||||
.ops = &arm_smmu_blocked_ops,
|
||||
};
|
||||
|
||||
static int arm_smmu_map_pages(struct iommu_domain *domain, unsigned long iova,
|
||||
phys_addr_t paddr, size_t pgsize, size_t pgcount,
|
||||
int prot, gfp_t gfp, size_t *mapped)
|
||||
@ -1560,8 +1605,10 @@ static int arm_smmu_def_domain_type(struct device *dev)
|
||||
}
|
||||
|
||||
static struct iommu_ops arm_smmu_ops = {
|
||||
.identity_domain = &arm_smmu_identity_domain,
|
||||
.blocked_domain = &arm_smmu_blocked_domain,
|
||||
.capable = arm_smmu_capable,
|
||||
.domain_alloc = arm_smmu_domain_alloc,
|
||||
.domain_alloc_paging = arm_smmu_domain_alloc_paging,
|
||||
.probe_device = arm_smmu_probe_device,
|
||||
.release_device = arm_smmu_release_device,
|
||||
.probe_finalize = arm_smmu_probe_finalize,
|
||||
|
@ -361,7 +361,6 @@ enum arm_smmu_domain_stage {
|
||||
ARM_SMMU_DOMAIN_S1 = 0,
|
||||
ARM_SMMU_DOMAIN_S2,
|
||||
ARM_SMMU_DOMAIN_NESTED,
|
||||
ARM_SMMU_DOMAIN_BYPASS,
|
||||
};
|
||||
|
||||
struct arm_smmu_domain {
|
||||
|
@ -900,8 +900,16 @@ static void qcom_iommu_device_remove(struct platform_device *pdev)
|
||||
static int __maybe_unused qcom_iommu_resume(struct device *dev)
|
||||
{
|
||||
struct qcom_iommu_dev *qcom_iommu = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
return clk_bulk_prepare_enable(CLK_NUM, qcom_iommu->clks);
|
||||
ret = clk_bulk_prepare_enable(CLK_NUM, qcom_iommu->clks);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (dev->pm_domain)
|
||||
return qcom_scm_restore_sec_cfg(qcom_iommu->sec_id, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __maybe_unused qcom_iommu_suspend(struct device *dev)
|
||||
|
Loading…
x
Reference in New Issue
Block a user