From 792f992308ff1a52614459c6091e363fb90b7fa8 Mon Sep 17 00:00:00 2001 From: Mitchel Humpherys Date: Tue, 23 Dec 2014 16:01:43 -0800 Subject: [PATCH] iommu/arm-smmu: add support for saving config registers Some hardware is capable of retaining register values during power collapse. Add an option (configurable via DT) to enable this feature. This is implemented by always enabling/disabling regulators every time clocks are enabled/disabled. Change-Id: I89d9a4f4a2eb29f0868b309d55a77cc4ed50e22e Signed-off-by: Mitchel Humpherys --- .../devicetree/bindings/iommu/arm,smmu.txt | 4 ++ drivers/iommu/arm-smmu.c | 66 +++++++++++-------- 2 files changed, 42 insertions(+), 28 deletions(-) diff --git a/Documentation/devicetree/bindings/iommu/arm,smmu.txt b/Documentation/devicetree/bindings/iommu/arm,smmu.txt index 7ed173cbdb0c..f9af001ad57d 100644 --- a/Documentation/devicetree/bindings/iommu/arm,smmu.txt +++ b/Documentation/devicetree/bindings/iommu/arm,smmu.txt @@ -64,6 +64,10 @@ conditions. implementations that require a halt and TLB invalidate before performing ATOS operations. +- qcom,register-save : Enable register saving awareness. This causes the + driver to assume that configuration registers will retain + their values across gdsc power gating. + - clocks : List of clocks to be used during SMMU register access. See Documentation/devicetree/bindings/clock/clock-bindings.txt for information about the format. For each clock specified diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 61558f2f14bf..27ec70a1b9f1 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -421,6 +421,7 @@ struct arm_smmu_device { #define ARM_SMMU_OPT_SECURE_CFG_ACCESS (1 << 0) #define ARM_SMMU_OPT_INVALIDATE_ON_MAP (1 << 1) #define ARM_SMMU_OPT_HALT_AND_TLB_ON_ATOS (1 << 2) +#define ARM_SMMU_OPT_REGISTER_SAVE (1 << 3) u32 options; enum arm_smmu_arch_version version; @@ -488,6 +489,7 @@ static struct arm_smmu_option_prop arm_smmu_options[] = { { ARM_SMMU_OPT_SECURE_CFG_ACCESS, "calxeda,smmu-secure-config-access" }, { ARM_SMMU_OPT_INVALIDATE_ON_MAP, "qcom,smmu-invalidate-on-map" }, { ARM_SMMU_OPT_HALT_AND_TLB_ON_ATOS, "qcom,halt-and-tlb-on-atos" }, + { ARM_SMMU_OPT_REGISTER_SAVE, "qcom,register-save" }, { 0, NULL}, }; @@ -719,31 +721,6 @@ static void __arm_smmu_free_bitmap(unsigned long *map, int idx) clear_bit(idx, map); } -static int arm_smmu_enable_clocks(struct arm_smmu_device *smmu) -{ - int i, ret = 0; - - for (i = 0; i < smmu->num_clocks; ++i) { - ret = clk_prepare_enable(smmu->clocks[i]); - if (ret) { - dev_err(smmu->dev, "Couldn't enable clock #%d\n", i); - while (i--) - clk_disable_unprepare(smmu->clocks[i]); - break; - } - } - - return ret; -} - -static void arm_smmu_disable_clocks(struct arm_smmu_device *smmu) -{ - int i; - - for (i = 0; i < smmu->num_clocks; ++i) - clk_disable_unprepare(smmu->clocks[i]); -} - static int arm_smmu_enable_regulators(struct arm_smmu_device *smmu) { if (!smmu->gdsc) @@ -760,6 +737,36 @@ static int arm_smmu_disable_regulators(struct arm_smmu_device *smmu) return regulator_disable(smmu->gdsc); } +static int arm_smmu_enable_clocks(struct arm_smmu_device *smmu) +{ + int i, ret = 0; + + arm_smmu_enable_regulators(smmu); + + for (i = 0; i < smmu->num_clocks; ++i) { + ret = clk_prepare_enable(smmu->clocks[i]); + if (ret) { + dev_err(smmu->dev, "Couldn't enable clock #%d\n", i); + while (i--) + clk_disable_unprepare(smmu->clocks[i]); + arm_smmu_disable_regulators(smmu); + break; + } + } + + return ret; +} + +static void arm_smmu_disable_clocks(struct arm_smmu_device *smmu) +{ + int i; + + for (i = 0; i < smmu->num_clocks; ++i) + clk_disable_unprepare(smmu->clocks[i]); + + arm_smmu_disable_regulators(smmu); +} + /* Wait for any pending TLB invalidations to complete */ static void arm_smmu_tlb_sync(struct arm_smmu_device *smmu) { @@ -1440,7 +1447,8 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) mutex_lock(&smmu->attach_lock); if (!smmu->attach_count++) { - arm_smmu_enable_regulators(smmu); + if (!(smmu->options & ARM_SMMU_OPT_REGISTER_SAVE)) + arm_smmu_enable_regulators(smmu); arm_smmu_enable_clocks(smmu); arm_smmu_device_reset(smmu); arm_smmu_impl_def_programming(smmu); @@ -1496,7 +1504,8 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev) err_disable_clocks: arm_smmu_disable_clocks(smmu); mutex_lock(&smmu->attach_lock); - if (!--smmu->attach_count) + if (!--smmu->attach_count && + (!(smmu->options & ARM_SMMU_OPT_REGISTER_SAVE))) arm_smmu_disable_regulators(smmu); mutex_unlock(&smmu->attach_lock); return ret; @@ -1509,7 +1518,8 @@ 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); - arm_smmu_disable_regulators(smmu); + if (!(smmu->options & ARM_SMMU_OPT_REGISTER_SAVE)) + arm_smmu_disable_regulators(smmu); } static void arm_smmu_detach_dev(struct iommu_domain *domain, struct device *dev)