Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a32e434e authored by Susheel Khiani's avatar Susheel Khiani
Browse files

iommu/arm-smmu: add option to enable halt/resume of SMMU



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: default avatarSusheel Khiani <skhiani@codeaurora.org>
parent bb29ea15
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -110,6 +110,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
+51 −0
Original line number Diff line number Diff line
@@ -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);