Skip to content

Commit c06334f

Browse files
committed
FROMLIST: iommu/arm-smmu: Add interconnect bandwidth voting support
On some SoCs the SMMU registers require an active interconnect bandwidth vote to be accessible. While other clients typically satisfy this requirement implicitly, certain corner cases (e.g. during sleep/wakeup transitions) can leave the SMMU without a vote, causing intermittent register access failures. Add support for an optional interconnect path to the arm-smmu driver and vote for bandwidth while the SMMU is active. The path is acquired from DT if present and ignored otherwise. The bandwidth vote is enabled before accessing SMMU registers during probe and runtime resume, and released during runtime suspend and on error paths. Generally, from an architectural perspective, GEM_NOC and DDR are expected to have an active vote whenever the adreno_smmu block is powered on. In most common use cases, this requirement is implicitly satisfied because other GPU-related clients (for example, the GMU device) already hold a GEM_NOC vote when adreno_smmu is enabled. However, there are certain corner cases, such as during sleep/wakeup transitions, where the GEM_NOC vote can be removed before adreno_smmu is powered down. If adreno_smmu is then accessed while the interconnect vote is missing, it can lead to the observed failures. Because of the precise ordering involved, this scenario is difficult to reproduce consistently. (also GDSC is involved in adreno usecases can have an independent vote) Link: https://lore.kernel.org/all/20260516-smmu_interconnect_addition-v1-2-f889d933f5c1@oss.qualcomm.com/ Signed-off-by: Bibek Kumar Patro <bibek.patro@oss.qualcomm.com>
1 parent a4ef6db commit c06334f

2 files changed

Lines changed: 54 additions & 1 deletion

File tree

drivers/iommu/arm/arm-smmu/arm-smmu.c

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,11 @@
5353
#define MSI_IOVA_BASE 0x8000000
5454
#define MSI_IOVA_LENGTH 0x100000
5555

56+
/* Interconnect bandwidth vote values for the SMMU register access path */
57+
#define ARM_SMMU_ICC_AVG_BW 0
58+
#define ARM_SMMU_ICC_PEAK_BW_HIGH 1000
59+
#define ARM_SMMU_ICC_PEAK_BW_LOW 0
60+
5661
static int force_stage;
5762
module_param(force_stage, int, S_IRUGO);
5863
MODULE_PARM_DESC(force_stage,
@@ -86,6 +91,36 @@ static inline void arm_smmu_rpm_put(struct arm_smmu_device *smmu)
8691
}
8792
}
8893

94+
static int arm_smmu_icc_get(struct arm_smmu_device *smmu)
95+
{
96+
smmu->icc_path = devm_of_icc_get(smmu->dev, NULL);
97+
if (IS_ERR(smmu->icc_path)) {
98+
int err = PTR_ERR(smmu->icc_path);
99+
100+
if (err == -ENODATA) {
101+
smmu->icc_path = NULL;
102+
return 0;
103+
}
104+
return dev_err_probe(smmu->dev, err,
105+
"failed to get interconnect path\n");
106+
}
107+
return 0;
108+
}
109+
110+
static void arm_smmu_icc_enable(struct arm_smmu_device *smmu)
111+
{
112+
if (smmu->icc_path)
113+
WARN_ON(icc_set_bw(smmu->icc_path, ARM_SMMU_ICC_AVG_BW,
114+
ARM_SMMU_ICC_PEAK_BW_HIGH));
115+
}
116+
117+
static void arm_smmu_icc_disable(struct arm_smmu_device *smmu)
118+
{
119+
if (smmu->icc_path)
120+
WARN_ON(icc_set_bw(smmu->icc_path, ARM_SMMU_ICC_AVG_BW,
121+
ARM_SMMU_ICC_PEAK_BW_LOW));
122+
}
123+
89124
static void arm_smmu_rpm_use_autosuspend(struct arm_smmu_device *smmu)
90125
{
91126
/*
@@ -2209,6 +2244,17 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
22092244
if (err)
22102245
return err;
22112246

2247+
/*
2248+
* Acquire and vote the interconnect path before accessing any SMMU
2249+
* registers (including ARM_SMMU_GR0_ID0 in arm_smmu_device_cfg_probe).
2250+
*/
2251+
err = arm_smmu_icc_get(smmu);
2252+
if (err) {
2253+
clk_bulk_disable_unprepare(smmu->num_clks, smmu->clks);
2254+
return err;
2255+
}
2256+
arm_smmu_icc_enable(smmu);
2257+
22122258
err = arm_smmu_device_cfg_probe(smmu);
22132259
if (err)
22142260
return err;
@@ -2309,9 +2355,13 @@ static int __maybe_unused arm_smmu_runtime_resume(struct device *dev)
23092355
struct arm_smmu_device *smmu = dev_get_drvdata(dev);
23102356
int ret;
23112357

2358+
arm_smmu_icc_enable(smmu);
2359+
23122360
ret = clk_bulk_enable(smmu->num_clks, smmu->clks);
2313-
if (ret)
2361+
if (ret) {
2362+
arm_smmu_icc_disable(smmu);
23142363
return ret;
2364+
}
23152365

23162366
arm_smmu_device_reset(smmu);
23172367

@@ -2323,6 +2373,7 @@ static int __maybe_unused arm_smmu_runtime_suspend(struct device *dev)
23232373
struct arm_smmu_device *smmu = dev_get_drvdata(dev);
23242374

23252375
clk_bulk_disable(smmu->num_clks, smmu->clks);
2376+
arm_smmu_icc_disable(smmu);
23262377

23272378
return 0;
23282379
}

drivers/iommu/arm/arm-smmu/arm-smmu.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <linux/bits.h>
1616
#include <linux/clk.h>
1717
#include <linux/device.h>
18+
#include <linux/interconnect.h>
1819
#include <linux/io-64-nonatomic-hi-lo.h>
1920
#include <linux/io-pgtable.h>
2021
#include <linux/iommu.h>
@@ -335,6 +336,7 @@ struct arm_smmu_device {
335336
int num_clks;
336337
unsigned int *irqs;
337338
struct clk_bulk_data *clks;
339+
struct icc_path *icc_path;
338340

339341
spinlock_t global_sync_lock;
340342

0 commit comments

Comments
 (0)