Merge "iommu: arm-smmu: Fix clock reference count error"

This commit is contained in:
Linux Build Service Account 2016-12-09 19:59:38 -08:00 committed by Gerrit - the friendly Code Review server
commit 2528ee722f

View file

@ -407,6 +407,9 @@ struct arm_smmu_device {
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;
@ -919,17 +922,43 @@ static int arm_smmu_unrequest_bus(struct arm_smmu_device *smmu)
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))
@ -944,6 +973,8 @@ static int arm_smmu_enable_regulators(struct arm_smmu_device *smmu)
if (WARN_ON_ONCE(ret))
goto out_bus;
smmu->power_count = 1;
mutex_unlock(&smmu->power_lock);
return ret;
out_bus:
@ -952,6 +983,7 @@ out_reg:
if (smmu->gdsc)
regulator_disable(smmu->gdsc);
out:
mutex_unlock(&smmu->power_lock);
return ret;
}
@ -2217,8 +2249,7 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
* 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;
@ -2235,12 +2266,18 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
}
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;
}
@ -2248,13 +2285,13 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
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
@ -2280,12 +2317,15 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
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);
@ -2293,8 +2333,7 @@ err_unlock:
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))
@ -2302,8 +2341,7 @@ static void arm_smmu_power_off(struct arm_smmu_device *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);
}
@ -2345,6 +2383,8 @@ static void arm_smmu_detach_dev(struct iommu_domain *domain, struct device *dev)
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;
}
@ -2358,7 +2398,9 @@ static void arm_smmu_detach_dev(struct iommu_domain *domain, struct device *dev)
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);
@ -3789,6 +3831,7 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
}
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);
@ -3970,7 +4013,7 @@ static int arm_smmu_device_remove(struct platform_device *pdev)
* 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);