Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions Documentation/devicetree/bindings/iommu/arm,smmu.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,20 @@ properties:
minItems: 1
maxItems: 3

interconnects:
maxItems: 1
description:
Optional interconnect path to the SMMU register space. On some SoCs
the SMMU registers are only accessible after a bandwidth vote has been
placed on the interconnect fabric. When present the driver votes for
bandwidth on this path before accessing any SMMU registers and releases
the vote on runtime suspend.

interconnect-names:
maxItems: 1
items:
- description: Name of the interconnect path to the SMMU register space.

nvidia,memory-controller:
description: |
A phandle to the memory controller on NVIDIA Tegra186 and later SoCs.
Expand Down
3 changes: 3 additions & 0 deletions arch/arm64/boot/dts/qcom/kodiak.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -3276,6 +3276,9 @@

power-domains = <&gpucc GPU_CC_CX_GDSC>;
dma-coherent;
interconnects = <&gem_noc MASTER_GPU_TCU QCOM_ICC_TAG_ALWAYS
&mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>;
interconnect-names = "gpu-smmu-mem";
};

gfx_0_tbu: tbu@3dd9000 {
Expand Down
53 changes: 52 additions & 1 deletion drivers/iommu/arm/arm-smmu/arm-smmu.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@
#define MSI_IOVA_BASE 0x8000000
#define MSI_IOVA_LENGTH 0x100000

/* Interconnect bandwidth vote values for the SMMU register access path */
#define ARM_SMMU_ICC_AVG_BW 0
#define ARM_SMMU_ICC_PEAK_BW_HIGH 1000
#define ARM_SMMU_ICC_PEAK_BW_LOW 0

static int force_stage;
module_param(force_stage, int, S_IRUGO);
MODULE_PARM_DESC(force_stage,
Expand Down Expand Up @@ -86,6 +91,36 @@ static inline void arm_smmu_rpm_put(struct arm_smmu_device *smmu)
}
}

static int arm_smmu_icc_get(struct arm_smmu_device *smmu)
{
smmu->icc_path = devm_of_icc_get(smmu->dev, NULL);
if (IS_ERR(smmu->icc_path)) {
int err = PTR_ERR(smmu->icc_path);

if (err == -ENODATA) {
smmu->icc_path = NULL;
return 0;
}
return dev_err_probe(smmu->dev, err,
"failed to get interconnect path\n");
}
return 0;
}

static void arm_smmu_icc_enable(struct arm_smmu_device *smmu)
{
if (smmu->icc_path)
WARN_ON(icc_set_bw(smmu->icc_path, ARM_SMMU_ICC_AVG_BW,
ARM_SMMU_ICC_PEAK_BW_HIGH));
}

static void arm_smmu_icc_disable(struct arm_smmu_device *smmu)
{
if (smmu->icc_path)
WARN_ON(icc_set_bw(smmu->icc_path, ARM_SMMU_ICC_AVG_BW,
ARM_SMMU_ICC_PEAK_BW_LOW));
}

static void arm_smmu_rpm_use_autosuspend(struct arm_smmu_device *smmu)
{
/*
Expand Down Expand Up @@ -2189,6 +2224,17 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
if (err)
return err;

/*
* Acquire and vote the interconnect path before accessing any SMMU
* registers (including ARM_SMMU_GR0_ID0 in arm_smmu_device_cfg_probe).
*/
err = arm_smmu_icc_get(smmu);
if (err) {
clk_bulk_disable_unprepare(smmu->num_clks, smmu->clks);
return err;
}
arm_smmu_icc_enable(smmu);

err = arm_smmu_device_cfg_probe(smmu);
if (err)
return err;
Expand Down Expand Up @@ -2294,9 +2340,13 @@ static int __maybe_unused arm_smmu_runtime_resume(struct device *dev)
struct arm_smmu_device *smmu = dev_get_drvdata(dev);
int ret;

arm_smmu_icc_enable(smmu);

ret = clk_bulk_enable(smmu->num_clks, smmu->clks);
if (ret)
if (ret) {
arm_smmu_icc_disable(smmu);
return ret;
}

arm_smmu_device_reset(smmu);

Expand All @@ -2308,6 +2358,7 @@ static int __maybe_unused arm_smmu_runtime_suspend(struct device *dev)
struct arm_smmu_device *smmu = dev_get_drvdata(dev);

clk_bulk_disable(smmu->num_clks, smmu->clks);
arm_smmu_icc_disable(smmu);

return 0;
}
Expand Down
2 changes: 2 additions & 0 deletions drivers/iommu/arm/arm-smmu/arm-smmu.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/interconnect.h>
#include <linux/io-64-nonatomic-hi-lo.h>
#include <linux/io-pgtable.h>
#include <linux/iommu.h>
Expand Down Expand Up @@ -335,6 +336,7 @@ struct arm_smmu_device {
int num_clks;
unsigned int *irqs;
struct clk_bulk_data *clks;
struct icc_path *icc_path;

spinlock_t global_sync_lock;

Expand Down