Arm SMMU updates for 5.20
- Add even more Qualcomm device-tree compatible strings - Support dumping of IMP DEF Qualcomm registers on TLB sync timeout - Fix reference count leak on device tree node in Qualcomm driver -----BEGIN PGP SIGNATURE----- iQFEBAABCgAuFiEEPxTL6PPUbjXGY88ct6xw3ITBYzQFAmLXDEoQHHdpbGxAa2Vy bmVsLm9yZwAKCRC3rHDchMFjNKc/B/950/2gtJLqPnxUlR0Jy2KzilNBuJagOIqy JveuwkW8MuMYIyNd8fpxMSTfQntXYsl+SGWtlh6yk7Xl5zZqki7YSu1VtRZJBiPk LSsD0hLa2Ev4qsEbD2Nbesx6VugXfOQ5bZdnA5WhxFwJoPp7/bTsBnBeEkr36d04 Jpjv2svbi2heaU7VqOLQoAzuD45FjDgAjFAPJblC+3dYNHi8KTGtkSEzc9hzFtCZ 1XiqNmV91PtESOuBiB8UIzNXpV5H8avnCSbST2VUvm3oHOGXpjh01l+8jlo0S9by 1JF5j6w99/vDRPs7JK+M3tmpuuMktav8GyMNP/2iFVhRwbAMI99O =I3nv -----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 5.20 - Add even more Qualcomm device-tree compatible strings - Support dumping of IMP DEF Qualcomm registers on TLB sync timeout - Fix reference count leak on device tree node in Qualcomm driver
This commit is contained in:
commit
d4ebd11f32
@ -42,6 +42,7 @@ properties:
|
||||
- qcom,sdx55-smmu-500
|
||||
- qcom,sdx65-smmu-500
|
||||
- qcom,sm6350-smmu-500
|
||||
- qcom,sm6375-smmu-500
|
||||
- qcom,sm8150-smmu-500
|
||||
- qcom,sm8250-smmu-500
|
||||
- qcom,sm8350-smmu-500
|
||||
|
@ -363,6 +363,16 @@ config ARM_SMMU_QCOM
|
||||
When running on a Qualcomm platform that has the custom variant
|
||||
of the ARM SMMU, this needs to be built into the SMMU driver.
|
||||
|
||||
config ARM_SMMU_QCOM_DEBUG
|
||||
bool "ARM SMMU QCOM implementation defined debug support"
|
||||
depends on ARM_SMMU_QCOM
|
||||
help
|
||||
Support for implementation specific debug features in ARM SMMU
|
||||
hardware found in QTI platforms.
|
||||
|
||||
Say Y here to enable debug for issues such as TLB sync timeouts
|
||||
which requires implementation defined register dumps.
|
||||
|
||||
config ARM_SMMU_V3
|
||||
tristate "ARM Ltd. System MMU Version 3 (SMMUv3) Support"
|
||||
depends on ARM64
|
||||
|
@ -3,3 +3,4 @@ obj-$(CONFIG_QCOM_IOMMU) += qcom_iommu.o
|
||||
obj-$(CONFIG_ARM_SMMU) += arm_smmu.o
|
||||
arm_smmu-objs += arm-smmu.o arm-smmu-impl.o arm-smmu-nvidia.o
|
||||
arm_smmu-$(CONFIG_ARM_SMMU_QCOM) += arm-smmu-qcom.o
|
||||
arm_smmu-$(CONFIG_ARM_SMMU_QCOM_DEBUG) += arm-smmu-qcom-debug.o
|
||||
|
142
drivers/iommu/arm/arm-smmu/arm-smmu-qcom-debug.c
Normal file
142
drivers/iommu/arm/arm-smmu/arm-smmu-qcom-debug.c
Normal file
@ -0,0 +1,142 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/qcom_scm.h>
|
||||
#include <linux/ratelimit.h>
|
||||
|
||||
#include "arm-smmu.h"
|
||||
#include "arm-smmu-qcom.h"
|
||||
|
||||
enum qcom_smmu_impl_reg_offset {
|
||||
QCOM_SMMU_TBU_PWR_STATUS,
|
||||
QCOM_SMMU_STATS_SYNC_INV_TBU_ACK,
|
||||
QCOM_SMMU_MMU2QSS_AND_SAFE_WAIT_CNTR,
|
||||
};
|
||||
|
||||
struct qcom_smmu_config {
|
||||
const u32 *reg_offset;
|
||||
};
|
||||
|
||||
void qcom_smmu_tlb_sync_debug(struct arm_smmu_device *smmu)
|
||||
{
|
||||
int ret;
|
||||
u32 tbu_pwr_status, sync_inv_ack, sync_inv_progress;
|
||||
struct qcom_smmu *qsmmu = container_of(smmu, struct qcom_smmu, smmu);
|
||||
const struct qcom_smmu_config *cfg;
|
||||
static DEFINE_RATELIMIT_STATE(rs, DEFAULT_RATELIMIT_INTERVAL,
|
||||
DEFAULT_RATELIMIT_BURST);
|
||||
|
||||
if (__ratelimit(&rs)) {
|
||||
dev_err(smmu->dev, "TLB sync timed out -- SMMU may be deadlocked\n");
|
||||
|
||||
cfg = qsmmu->cfg;
|
||||
if (!cfg)
|
||||
return;
|
||||
|
||||
ret = qcom_scm_io_readl(smmu->ioaddr + cfg->reg_offset[QCOM_SMMU_TBU_PWR_STATUS],
|
||||
&tbu_pwr_status);
|
||||
if (ret)
|
||||
dev_err(smmu->dev,
|
||||
"Failed to read TBU power status: %d\n", ret);
|
||||
|
||||
ret = qcom_scm_io_readl(smmu->ioaddr + cfg->reg_offset[QCOM_SMMU_STATS_SYNC_INV_TBU_ACK],
|
||||
&sync_inv_ack);
|
||||
if (ret)
|
||||
dev_err(smmu->dev,
|
||||
"Failed to read TBU sync/inv ack status: %d\n", ret);
|
||||
|
||||
ret = qcom_scm_io_readl(smmu->ioaddr + cfg->reg_offset[QCOM_SMMU_MMU2QSS_AND_SAFE_WAIT_CNTR],
|
||||
&sync_inv_progress);
|
||||
if (ret)
|
||||
dev_err(smmu->dev,
|
||||
"Failed to read TCU syn/inv progress: %d\n", ret);
|
||||
|
||||
dev_err(smmu->dev,
|
||||
"TBU: power_status %#x sync_inv_ack %#x sync_inv_progress %#x\n",
|
||||
tbu_pwr_status, sync_inv_ack, sync_inv_progress);
|
||||
}
|
||||
}
|
||||
|
||||
/* Implementation Defined Register Space 0 register offsets */
|
||||
static const u32 qcom_smmu_impl0_reg_offset[] = {
|
||||
[QCOM_SMMU_TBU_PWR_STATUS] = 0x2204,
|
||||
[QCOM_SMMU_STATS_SYNC_INV_TBU_ACK] = 0x25dc,
|
||||
[QCOM_SMMU_MMU2QSS_AND_SAFE_WAIT_CNTR] = 0x2670,
|
||||
};
|
||||
|
||||
static const struct qcom_smmu_config qcm2290_smmu_cfg = {
|
||||
.reg_offset = qcom_smmu_impl0_reg_offset,
|
||||
};
|
||||
|
||||
static const struct qcom_smmu_config sc7180_smmu_cfg = {
|
||||
.reg_offset = qcom_smmu_impl0_reg_offset,
|
||||
};
|
||||
|
||||
static const struct qcom_smmu_config sc7280_smmu_cfg = {
|
||||
.reg_offset = qcom_smmu_impl0_reg_offset,
|
||||
};
|
||||
|
||||
static const struct qcom_smmu_config sc8180x_smmu_cfg = {
|
||||
.reg_offset = qcom_smmu_impl0_reg_offset,
|
||||
};
|
||||
|
||||
static const struct qcom_smmu_config sc8280xp_smmu_cfg = {
|
||||
.reg_offset = qcom_smmu_impl0_reg_offset,
|
||||
};
|
||||
|
||||
static const struct qcom_smmu_config sm6125_smmu_cfg = {
|
||||
.reg_offset = qcom_smmu_impl0_reg_offset,
|
||||
};
|
||||
|
||||
static const struct qcom_smmu_config sm6350_smmu_cfg = {
|
||||
.reg_offset = qcom_smmu_impl0_reg_offset,
|
||||
};
|
||||
|
||||
static const struct qcom_smmu_config sm8150_smmu_cfg = {
|
||||
.reg_offset = qcom_smmu_impl0_reg_offset,
|
||||
};
|
||||
|
||||
static const struct qcom_smmu_config sm8250_smmu_cfg = {
|
||||
.reg_offset = qcom_smmu_impl0_reg_offset,
|
||||
};
|
||||
|
||||
static const struct qcom_smmu_config sm8350_smmu_cfg = {
|
||||
.reg_offset = qcom_smmu_impl0_reg_offset,
|
||||
};
|
||||
|
||||
static const struct qcom_smmu_config sm8450_smmu_cfg = {
|
||||
.reg_offset = qcom_smmu_impl0_reg_offset,
|
||||
};
|
||||
|
||||
static const struct of_device_id __maybe_unused qcom_smmu_impl_debug_match[] = {
|
||||
{ .compatible = "qcom,msm8998-smmu-v2" },
|
||||
{ .compatible = "qcom,qcm2290-smmu-500", .data = &qcm2290_smmu_cfg },
|
||||
{ .compatible = "qcom,sc7180-smmu-500", .data = &sc7180_smmu_cfg },
|
||||
{ .compatible = "qcom,sc7280-smmu-500", .data = &sc7280_smmu_cfg},
|
||||
{ .compatible = "qcom,sc8180x-smmu-500", .data = &sc8180x_smmu_cfg },
|
||||
{ .compatible = "qcom,sc8280xp-smmu-500", .data = &sc8280xp_smmu_cfg },
|
||||
{ .compatible = "qcom,sdm630-smmu-v2" },
|
||||
{ .compatible = "qcom,sdm845-smmu-500" },
|
||||
{ .compatible = "qcom,sm6125-smmu-500", .data = &sm6125_smmu_cfg},
|
||||
{ .compatible = "qcom,sm6350-smmu-500", .data = &sm6350_smmu_cfg},
|
||||
{ .compatible = "qcom,sm8150-smmu-500", .data = &sm8150_smmu_cfg },
|
||||
{ .compatible = "qcom,sm8250-smmu-500", .data = &sm8250_smmu_cfg },
|
||||
{ .compatible = "qcom,sm8350-smmu-500", .data = &sm8350_smmu_cfg },
|
||||
{ .compatible = "qcom,sm8450-smmu-500", .data = &sm8450_smmu_cfg },
|
||||
{ }
|
||||
};
|
||||
|
||||
const void *qcom_smmu_impl_data(struct arm_smmu_device *smmu)
|
||||
{
|
||||
const struct of_device_id *match;
|
||||
const struct device_node *np = smmu->dev->of_node;
|
||||
|
||||
match = of_match_node(qcom_smmu_impl_debug_match, np);
|
||||
if (!match)
|
||||
return NULL;
|
||||
|
||||
return match->data;
|
||||
}
|
@ -5,23 +5,40 @@
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/adreno-smmu-priv.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/qcom_scm.h>
|
||||
|
||||
#include "arm-smmu.h"
|
||||
#include "arm-smmu-qcom.h"
|
||||
|
||||
struct qcom_smmu {
|
||||
struct arm_smmu_device smmu;
|
||||
bool bypass_quirk;
|
||||
u8 bypass_cbndx;
|
||||
u32 stall_enabled;
|
||||
};
|
||||
#define QCOM_DUMMY_VAL -1
|
||||
|
||||
static struct qcom_smmu *to_qcom_smmu(struct arm_smmu_device *smmu)
|
||||
{
|
||||
return container_of(smmu, struct qcom_smmu, smmu);
|
||||
}
|
||||
|
||||
static void qcom_smmu_tlb_sync(struct arm_smmu_device *smmu, int page,
|
||||
int sync, int status)
|
||||
{
|
||||
unsigned int spin_cnt, delay;
|
||||
u32 reg;
|
||||
|
||||
arm_smmu_writel(smmu, page, sync, QCOM_DUMMY_VAL);
|
||||
for (delay = 1; delay < TLB_LOOP_TIMEOUT; delay *= 2) {
|
||||
for (spin_cnt = TLB_SPIN_COUNT; spin_cnt > 0; spin_cnt--) {
|
||||
reg = arm_smmu_readl(smmu, page, status);
|
||||
if (!(reg & ARM_SMMU_sTLBGSTATUS_GSACTIVE))
|
||||
return;
|
||||
cpu_relax();
|
||||
}
|
||||
udelay(delay);
|
||||
}
|
||||
|
||||
qcom_smmu_tlb_sync_debug(smmu);
|
||||
}
|
||||
|
||||
static void qcom_adreno_smmu_write_sctlr(struct arm_smmu_device *smmu, int idx,
|
||||
u32 reg)
|
||||
{
|
||||
@ -233,6 +250,7 @@ static const struct of_device_id qcom_smmu_client_of_match[] __maybe_unused = {
|
||||
{ .compatible = "qcom,sc7280-mdss" },
|
||||
{ .compatible = "qcom,sc7280-mss-pil" },
|
||||
{ .compatible = "qcom,sc8180x-mdss" },
|
||||
{ .compatible = "qcom,sm8250-mdss" },
|
||||
{ .compatible = "qcom,sdm845-mdss" },
|
||||
{ .compatible = "qcom,sdm845-mss-pil" },
|
||||
{ }
|
||||
@ -374,6 +392,7 @@ static const struct arm_smmu_impl qcom_smmu_impl = {
|
||||
.def_domain_type = qcom_smmu_def_domain_type,
|
||||
.reset = qcom_smmu500_reset,
|
||||
.write_s2cr = qcom_smmu_write_s2cr,
|
||||
.tlb_sync = qcom_smmu_tlb_sync,
|
||||
};
|
||||
|
||||
static const struct arm_smmu_impl qcom_adreno_smmu_impl = {
|
||||
@ -382,6 +401,7 @@ static const struct arm_smmu_impl qcom_adreno_smmu_impl = {
|
||||
.reset = qcom_smmu500_reset,
|
||||
.alloc_context_bank = qcom_adreno_smmu_alloc_context_bank,
|
||||
.write_sctlr = qcom_adreno_smmu_write_sctlr,
|
||||
.tlb_sync = qcom_smmu_tlb_sync,
|
||||
};
|
||||
|
||||
static struct arm_smmu_device *qcom_smmu_create(struct arm_smmu_device *smmu,
|
||||
@ -398,6 +418,7 @@ static struct arm_smmu_device *qcom_smmu_create(struct arm_smmu_device *smmu,
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
qsmmu->smmu.impl = impl;
|
||||
qsmmu->cfg = qcom_smmu_impl_data(smmu);
|
||||
|
||||
return &qsmmu->smmu;
|
||||
}
|
||||
@ -413,6 +434,7 @@ static const struct of_device_id __maybe_unused qcom_smmu_impl_of_match[] = {
|
||||
{ .compatible = "qcom,sdm845-smmu-500" },
|
||||
{ .compatible = "qcom,sm6125-smmu-500" },
|
||||
{ .compatible = "qcom,sm6350-smmu-500" },
|
||||
{ .compatible = "qcom,sm6375-smmu-500" },
|
||||
{ .compatible = "qcom,sm8150-smmu-500" },
|
||||
{ .compatible = "qcom,sm8250-smmu-500" },
|
||||
{ .compatible = "qcom,sm8350-smmu-500" },
|
||||
|
28
drivers/iommu/arm/arm-smmu/arm-smmu-qcom.h
Normal file
28
drivers/iommu/arm/arm-smmu/arm-smmu-qcom.h
Normal file
@ -0,0 +1,28 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _ARM_SMMU_QCOM_H
|
||||
#define _ARM_SMMU_QCOM_H
|
||||
|
||||
struct qcom_smmu {
|
||||
struct arm_smmu_device smmu;
|
||||
const struct qcom_smmu_config *cfg;
|
||||
bool bypass_quirk;
|
||||
u8 bypass_cbndx;
|
||||
u32 stall_enabled;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_ARM_SMMU_QCOM_DEBUG
|
||||
void qcom_smmu_tlb_sync_debug(struct arm_smmu_device *smmu);
|
||||
const void *qcom_smmu_impl_data(struct arm_smmu_device *smmu);
|
||||
#else
|
||||
static inline void qcom_smmu_tlb_sync_debug(struct arm_smmu_device *smmu) { }
|
||||
static inline const void *qcom_smmu_impl_data(struct arm_smmu_device *smmu)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _ARM_SMMU_QCOM_H */
|
@ -2074,7 +2074,6 @@ err_reset_platform_ops: __maybe_unused;
|
||||
static int arm_smmu_device_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
resource_size_t ioaddr;
|
||||
struct arm_smmu_device *smmu;
|
||||
struct device *dev = &pdev->dev;
|
||||
int num_irqs, i, err;
|
||||
@ -2098,7 +2097,8 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
|
||||
smmu->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
|
||||
if (IS_ERR(smmu->base))
|
||||
return PTR_ERR(smmu->base);
|
||||
ioaddr = res->start;
|
||||
smmu->ioaddr = res->start;
|
||||
|
||||
/*
|
||||
* The resource size should effectively match the value of SMMU_TOP;
|
||||
* stash that temporarily until we know PAGESIZE to validate it with.
|
||||
@ -2178,7 +2178,7 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
err = iommu_device_sysfs_add(&smmu->iommu, smmu->dev, NULL,
|
||||
"smmu.%pa", &ioaddr);
|
||||
"smmu.%pa", &smmu->ioaddr);
|
||||
if (err) {
|
||||
dev_err(dev, "Failed to register iommu in sysfs\n");
|
||||
return err;
|
||||
|
@ -278,6 +278,7 @@ struct arm_smmu_device {
|
||||
struct device *dev;
|
||||
|
||||
void __iomem *base;
|
||||
phys_addr_t ioaddr;
|
||||
unsigned int numpage;
|
||||
unsigned int pgshift;
|
||||
|
||||
|
@ -750,9 +750,12 @@ static bool qcom_iommu_has_secure_context(struct qcom_iommu_dev *qcom_iommu)
|
||||
{
|
||||
struct device_node *child;
|
||||
|
||||
for_each_child_of_node(qcom_iommu->dev->of_node, child)
|
||||
if (of_device_is_compatible(child, "qcom,msm-iommu-v1-sec"))
|
||||
for_each_child_of_node(qcom_iommu->dev->of_node, child) {
|
||||
if (of_device_is_compatible(child, "qcom,msm-iommu-v1-sec")) {
|
||||
of_node_put(child);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user