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

Commit f8464885 authored by Charan Teja Reddy's avatar Charan Teja Reddy Committed by Gerrit - the friendly Code Review server
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: I3f096d578a79a070dad085ba7b882fd955dcc7bc
Signed-off-by: default avatarCharan Teja Reddy <charante@codeaurora.org>
parent 35144b0b
Loading
Loading
Loading
Loading
+74 −0
Original line number Diff line number Diff line
@@ -57,6 +57,7 @@
#include <trace/events/iommu.h>
#include <soc/qcom/msm_tz_smmu.h>
#include <soc/qcom/scm.h>
#include <linux/notifier.h>

#include <linux/amba/bus.h>

@@ -440,6 +441,7 @@ struct arm_smmu_device {
#define ARM_SMMU_OPT_DISABLE_ATOS	(1 << 6)
#define ARM_SMMU_OPT_MMU500_ERRATA1	(1 << 7)
#define ARM_SMMU_OPT_STATIC_CB		(1 << 8)
#define ARM_SMMU_OPT_HALT		(1 << 9)
	u32				options;
	enum arm_smmu_arch_version	version;
	enum arm_smmu_implementation	model;
@@ -473,6 +475,7 @@ struct arm_smmu_device {
	unsigned int			num_impl_def_attach_registers;

	struct arm_smmu_power_resources *pwr;
	struct notifier_block		regulator_nb;

	spinlock_t			atos_lock;

@@ -570,6 +573,7 @@ static struct arm_smmu_option_prop arm_smmu_options[] = {
	{ ARM_SMMU_OPT_DISABLE_ATOS, "qcom,disable-atos" },
	{ ARM_SMMU_OPT_MMU500_ERRATA1, "qcom,mmu500-errata-1" },
	{ ARM_SMMU_OPT_STATIC_CB, "qcom,enable-static-cb"},
	{ ARM_SMMU_OPT_HALT, "qcom,enable-smmu-halt"},
	{ 0, NULL},
};

@@ -3817,6 +3821,71 @@ static int arm_smmu_init_clocks(struct arm_smmu_power_resources *pwr)
	return 0;
}

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);

	if (event != REGULATOR_EVENT_PRE_DISABLE &&
	    event != REGULATOR_EVENT_ENABLE)
		return NOTIFY_OK;

	ret = arm_smmu_prepare_clocks(smmu->pwr);
	if (ret)
		goto out;

	ret = arm_smmu_power_on_atomic(smmu->pwr);
	if (ret)
		goto unprepare_clock;

	if (event == REGULATOR_EVENT_PRE_DISABLE)
		qsmmuv2_halt(smmu);
	else if (event == REGULATOR_EVENT_ENABLE) {
		if (arm_smmu_restore_sec_cfg(smmu))
			goto power_off;
		qsmmuv2_resume(smmu);
	}
power_off:
	arm_smmu_power_off_atomic(smmu->pwr);
unprepare_clock:
	arm_smmu_unprepare_clocks(smmu->pwr);
out:
	return NOTIFY_OK;
}

static int register_regulator_notifier(struct arm_smmu_device *smmu)
{
	struct device *dev = smmu->dev;
	struct regulator_bulk_data *consumers;
	int ret = 0, num_consumers;
	struct arm_smmu_power_resources *pwr = smmu->pwr;

	if (!(smmu->options & ARM_SMMU_OPT_HALT))
		goto out;

	num_consumers = pwr->num_gdscs;
	consumers = pwr->gdscs;

	if (!num_consumers) {
		dev_info(dev, "no regulator info exist for %s\n",
			 dev_name(dev));
		goto out;
	}

	smmu->regulator_nb.notifier_call = regulator_notifier;
	/* registering the notifier against one gdsc is sufficient as
	 * we do enable/disable regulators in group.
	 */
	ret = regulator_register_notifier(consumers[0].consumer,
					  &smmu->regulator_nb);
	if (ret)
		dev_err(dev, "Regulator notifier request failed\n");
out:
	return ret;
}

static int arm_smmu_init_regulators(struct arm_smmu_power_resources *pwr)
{
	const char *cname;
@@ -4377,6 +4446,11 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
	else
		bus_for_each_dev(&platform_bus_type, NULL, &arm_smmu_ops,
				 arm_smmu_add_device_fixup);

	err = register_regulator_notifier(smmu);
	if (err)
		goto out_power_off;

#ifdef CONFIG_ARM_AMBA
	if (!iommu_present(&amba_bustype))
		bus_set_iommu(&amba_bustype, &arm_smmu_ops);