OSDN Git Service

iommu/arm-smmu: add option to enable halt/resume of SMMU
authorSusheel Khiani <skhiani@codeaurora.org>
Tue, 6 Oct 2015 13:59:21 +0000 (19:29 +0530)
committerDavid Keitel <dkeitel@codeaurora.org>
Tue, 22 Mar 2016 18:14:30 +0000 (11:14 -0700)
Before SMMU is powered down, SMMU needs to be in
idle state prior to power collapse. When 'halt' is
received by SMMU, it ensures that no new requests
enters and all outstanding requests are completed
and generates an acknowledgment for halt request.

Add an option to register a notifier on regulator
so that SMMU can be halted/resumed when regulator
is powered down/up. Some of the targets have built
in provision in hardware to halt SMMU when there
are no on going transactions. We should keep this
option disable for those targets.

Change-Id: Ia2f8a934a9d64daefdacd517eb22f09de5eeb273
Signed-off-by: Susheel Khiani <skhiani@codeaurora.org>
Documentation/devicetree/bindings/iommu/arm,smmu.txt
drivers/iommu/arm-smmu.c

index 490fcb4..ab1b3d0 100644 (file)
@@ -116,6 +116,14 @@ conditions.
                  useful if the upstream hardware is capable of switching
                  between multiple domains within a single context bank.
 
+- qcom,enable-smmu-halt : Before SMMU is powered down, SMMU needs to be in
+                 idle state prior to power collapse. When 'halt' is received by
+                 SMMU, it ensures that no new requests enters and all
+                 outstanding requests are completed and generates an
+                 acknowledgment for halt request. So add an option to register
+                 a call back notifier on regulators in whcih SMMU can be halted
+                 or resumed when regulator is powered down/up.
+
 - 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
index 09761b7..540c0d1 100644 (file)
@@ -41,6 +41,7 @@
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/spinlock.h>
+#include <linux/notifier.h>
 
 #include <linux/amba/bus.h>
 #include <soc/qcom/msm_tz_smmu.h>
@@ -359,6 +360,7 @@ struct arm_smmu_device {
 #define ARM_SMMU_OPT_NO_M              (1 << 8)
 #define ARM_SMMU_OPT_NO_SMR_CHECK      (1 << 9)
 #define ARM_SMMU_OPT_DYNAMIC           (1 << 10)
+#define ARM_SMMU_OPT_HALT              (1 << 11)
        u32                             options;
        enum arm_smmu_arch_version      version;
 
@@ -385,6 +387,7 @@ struct arm_smmu_device {
        struct clk                      **clocks;
 
        struct regulator                *gdsc;
+       struct notifier_block           regulator_nb;
 
        /* Protects against domains attaching to the same SMMU concurrently */
        struct mutex                    attach_lock;
@@ -474,6 +477,7 @@ static struct arm_smmu_option_prop arm_smmu_options[] = {
        { ARM_SMMU_OPT_NO_M, "qcom,no-mmu-enable" },
        { ARM_SMMU_OPT_NO_SMR_CHECK, "qcom,no-smr-check" },
        { ARM_SMMU_OPT_DYNAMIC, "qcom,dynamic" },
+       { ARM_SMMU_OPT_HALT, "qcom,enable-smmu-halt"},
        { 0, NULL},
 };
 
@@ -2921,6 +2925,49 @@ static int arm_smmu_id_size_to_bits(int size)
        }
 }
 
+static int regulator_notifier(struct notifier_block *nb,
+               unsigned long event, void *data)
+{
+       int ret = 0;
+       struct arm_smmu_device *smmu = container_of(nb,
+                                       struct arm_smmu_device, regulator_nb);
+
+       ret = arm_smmu_prepare_clocks(smmu);
+       if (ret)
+               goto out;
+
+       ret = arm_smmu_enable_clocks_atomic(smmu);
+       if (ret)
+               goto unprepare_clock;
+
+       if (event == REGULATOR_EVENT_DISABLE)
+               arm_smmu_halt(smmu);
+       else if (event == REGULATOR_EVENT_ENABLE)
+               arm_smmu_resume(smmu);
+
+       arm_smmu_disable_clocks_atomic(smmu);
+unprepare_clock:
+       arm_smmu_unprepare_clocks(smmu);
+out:
+       return ret;
+}
+
+static int register_regulator_notifier(struct arm_smmu_device *smmu)
+{
+       struct device *dev = smmu->dev;
+       int ret = 0;
+
+       if (smmu->options & ARM_SMMU_OPT_HALT) {
+               smmu->regulator_nb.notifier_call = regulator_notifier;
+               ret = regulator_register_notifier(smmu->gdsc,
+                                               &smmu->regulator_nb);
+
+               if (ret)
+                       dev_err(dev, "Regulator notifier request failed\n");
+       }
+       return ret;
+}
+
 static int arm_smmu_init_regulators(struct arm_smmu_device *smmu)
 {
        struct device *dev = smmu->dev;
@@ -3349,6 +3396,10 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 
        idr_init(&smmu->asid_idr);
 
+       err = register_regulator_notifier(smmu);
+       if (err)
+               goto out_free_irqs;
+
        INIT_LIST_HEAD(&smmu->list);
        spin_lock(&arm_smmu_devices_lock);
        list_add(&smmu->list, &arm_smmu_devices);