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
#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>
#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;
struct clk **clocks;
struct regulator *gdsc;
+ struct notifier_block regulator_nb;
/* Protects against domains attaching to the same SMMU concurrently */
struct mutex attach_lock;
{ 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},
};
}
}
+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;
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);