unsigned int clock_refs_count;
spinlock_t clock_refs_lock;
+ struct mutex power_lock;
+ unsigned int power_count;
+
struct msm_bus_client_handle *bus_client;
char *bus_client_name;
static int arm_smmu_disable_regulators(struct arm_smmu_device *smmu)
{
+ int ret = 0;
+
+ mutex_lock(&smmu->power_lock);
+ if (smmu->power_count == 0) {
+ WARN(1, "%s: Mismatched power count\n", dev_name(smmu->dev));
+ mutex_unlock(&smmu->power_lock);
+ return -EINVAL;
+ } else if (smmu->power_count > 1) {
+ smmu->power_count -= 1;
+ mutex_unlock(&smmu->power_lock);
+ return 0;
+ }
+
arm_smmu_unprepare_clocks(smmu);
arm_smmu_unrequest_bus(smmu);
- if (!smmu->gdsc)
- return 0;
- return regulator_disable(smmu->gdsc);
+ if (smmu->gdsc) {
+ ret = regulator_disable(smmu->gdsc);
+ WARN(ret, "%s: Regulator disable failed\n",
+ dev_name(smmu->dev));
+ }
+
+ smmu->power_count = 0;
+ mutex_unlock(&smmu->power_lock);
+ return ret;
}
static int arm_smmu_enable_regulators(struct arm_smmu_device *smmu)
{
int ret;
+ mutex_lock(&smmu->power_lock);
+ if (smmu->power_count) {
+ smmu->power_count++;
+ mutex_unlock(&smmu->power_lock);
+ return 0;
+ }
+
if (smmu->gdsc) {
ret = regulator_enable(smmu->gdsc);
if (WARN_ON_ONCE(ret))
if (WARN_ON_ONCE(ret))
goto out_bus;
+ smmu->power_count = 1;
+ mutex_unlock(&smmu->power_lock);
return ret;
out_bus:
if (smmu->gdsc)
regulator_disable(smmu->gdsc);
out:
+ mutex_unlock(&smmu->power_lock);
return ret;
}
* refcounting) is that we never disable regulators while a
* client is attached in these cases.
*/
- if (!(smmu->options & ARM_SMMU_OPT_REGISTER_SAVE) ||
- atomic_ctx) {
+ if (!(smmu->options & ARM_SMMU_OPT_REGISTER_SAVE)) {
ret = arm_smmu_enable_regulators(smmu);
if (ret)
goto err_unlock;
}
smmu->attach_count++;
+ if (atomic_ctx) {
+ ret = arm_smmu_enable_regulators(smmu);
+ if (ret)
+ goto err_disable_clocks;
+ }
+
if (arm_smmu_is_static_cb(smmu)) {
ret = arm_smmu_populate_cb(smmu, smmu_domain, dev);
if (ret) {
dev_err(dev, "Failed to get valid context bank\n");
- goto err_disable_clocks;
+ goto err_atomic_ctx;
}
smmu_domain->slave_side_secure = true;
}
cfg = find_smmu_master_cfg(dev);
if (!cfg) {
ret = -ENODEV;
- goto err_disable_clocks;
+ goto err_atomic_ctx;
}
/* Ensure that the domain is finalised */
ret = arm_smmu_init_domain_context(domain, smmu, cfg);
if (IS_ERR_VALUE(ret))
- goto err_disable_clocks;
+ goto err_atomic_ctx;
/*
* Sanity check the domain. We don't support domains across
err_destroy_domain_context:
arm_smmu_destroy_domain_context(domain);
+err_atomic_ctx:
+ if (atomic_ctx)
+ arm_smmu_disable_regulators(smmu);
err_disable_clocks:
arm_smmu_disable_clocks(smmu);
--smmu->attach_count;
err_disable_regulators:
if (!smmu->attach_count &&
- (!(smmu->options & ARM_SMMU_OPT_REGISTER_SAVE) || atomic_ctx))
+ (!(smmu->options & ARM_SMMU_OPT_REGISTER_SAVE)))
arm_smmu_disable_regulators(smmu);
err_unlock:
mutex_unlock(&smmu->attach_lock);
return ret;
}
-static void arm_smmu_power_off(struct arm_smmu_device *smmu,
- bool force_regulator_disable)
+static void arm_smmu_power_off(struct arm_smmu_device *smmu)
{
/* Turn the thing off */
if (arm_smmu_enable_clocks(smmu))
writel_relaxed(sCR0_CLIENTPD,
ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sCR0);
arm_smmu_disable_clocks(smmu);
- if (!(smmu->options & ARM_SMMU_OPT_REGISTER_SAVE)
- || force_regulator_disable)
+ if (!(smmu->options & ARM_SMMU_OPT_REGISTER_SAVE))
arm_smmu_disable_regulators(smmu);
}
if (smmu_domain->attributes & (1 << DOMAIN_ATTR_DYNAMIC)) {
arm_smmu_detach_dynamic(domain, smmu);
mutex_unlock(&smmu_domain->init_mutex);
+ if (atomic_ctx)
+ arm_smmu_disable_regulators(smmu);
return;
}
arm_smmu_domain_remove_master(smmu_domain, cfg);
arm_smmu_destroy_domain_context(domain);
if (!--smmu->attach_count)
- arm_smmu_power_off(smmu, atomic_ctx);
+ arm_smmu_power_off(smmu);
+ if (atomic_ctx)
+ arm_smmu_disable_regulators(smmu);
unlock:
mutex_unlock(&smmu->attach_lock);
mutex_unlock(&smmu_domain->init_mutex);
}
smmu->dev = dev;
mutex_init(&smmu->attach_lock);
+ mutex_init(&smmu->power_lock);
spin_lock_init(&smmu->atos_lock);
spin_lock_init(&smmu->clock_refs_lock);
INIT_LIST_HEAD(&smmu->static_cbndx_list);
* still powered on. Power off now.
*/
if (smmu->attach_count)
- arm_smmu_power_off(smmu, false);
+ arm_smmu_power_off(smmu);
mutex_unlock(&smmu->attach_lock);
msm_bus_scale_unregister(smmu->bus_client);