[v2,2/5] iommu/arm-smmu: Emulate bypass by using context banks

Message ID 20200717001619.325317-3-bjorn.andersson@linaro.org
State New
Headers show
Series
  • iommu/arm-smmu: Support maintaining bootloader mappings
Related show

Commit Message

Bjorn Andersson July 17, 2020, 12:16 a.m.
Some firmware found on various Qualcomm platforms traps writes to S2CR
of type BYPASS and writes FAULT into the register. This prevents us from
marking the streams for the display controller as BYPASS to allow
continued scanout of the screen through the initialization of the ARM
SMMU.

This adds a Qualcomm specific cfg_probe function, which probes the
behavior of the S2CR registers and if found faulty enables the related
quirk. Based on this quirk context banks are allocated for IDENTITY
domains as well, but with ARM_SMMU_SCTLR_M omitted.

The result is valid stream mappings, without translation.

Tested-by: John Stultz <john.stultz@linaro.org>

Tested-by: Vinod Koul <vkoul@kernel.org>

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>

---

Changes since v1:
- Picked up tested-by

 drivers/iommu/arm-smmu-qcom.c | 21 +++++++++++++++++++++
 drivers/iommu/arm-smmu.c      | 14 ++++++++++++--
 drivers/iommu/arm-smmu.h      |  3 +++
 3 files changed, 36 insertions(+), 2 deletions(-)

-- 
2.26.2

Comments

Will Deacon July 20, 2020, 8:58 a.m. | #1
On Thu, Jul 16, 2020 at 05:16:16PM -0700, Bjorn Andersson wrote:
> Some firmware found on various Qualcomm platforms traps writes to S2CR

> of type BYPASS and writes FAULT into the register. This prevents us from

> marking the streams for the display controller as BYPASS to allow

> continued scanout of the screen through the initialization of the ARM

> SMMU.

> 

> This adds a Qualcomm specific cfg_probe function, which probes the

> behavior of the S2CR registers and if found faulty enables the related

> quirk. Based on this quirk context banks are allocated for IDENTITY

> domains as well, but with ARM_SMMU_SCTLR_M omitted.

> 

> The result is valid stream mappings, without translation.

> 

> Tested-by: John Stultz <john.stultz@linaro.org>

> Tested-by: Vinod Koul <vkoul@kernel.org>

> Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>

> ---

> 

> Changes since v1:

> - Picked up tested-by

> 

>  drivers/iommu/arm-smmu-qcom.c | 21 +++++++++++++++++++++

>  drivers/iommu/arm-smmu.c      | 14 ++++++++++++--

>  drivers/iommu/arm-smmu.h      |  3 +++

>  3 files changed, 36 insertions(+), 2 deletions(-)


[...]

> diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c

> index fb85e716ae9a..5d5fe6741ed4 100644

> --- a/drivers/iommu/arm-smmu.c

> +++ b/drivers/iommu/arm-smmu.c

> @@ -654,7 +654,9 @@ static void arm_smmu_write_context_bank(struct arm_smmu_device *smmu, int idx)

>  

>  	/* SCTLR */

>  	reg = ARM_SMMU_SCTLR_CFIE | ARM_SMMU_SCTLR_CFRE | ARM_SMMU_SCTLR_AFE |

> -	      ARM_SMMU_SCTLR_TRE | ARM_SMMU_SCTLR_M;

> +	      ARM_SMMU_SCTLR_TRE;

> +	if (cfg->m)

> +		reg |= ARM_SMMU_SCTLR_M;

>  	if (stage1)

>  		reg |= ARM_SMMU_SCTLR_S1_ASIDPNE;

>  	if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN))

> @@ -678,7 +680,11 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,

>  	if (smmu_domain->smmu)

>  		goto out_unlock;

>  

> -	if (domain->type == IOMMU_DOMAIN_IDENTITY) {

> +	/*

> +	 * Nothing to do for IDENTITY domains,unless disabled context banks are

> +	 * used to emulate bypass mappings on Qualcomm platforms.

> +	 */

> +	if (domain->type == IOMMU_DOMAIN_IDENTITY && !smmu->qcom_bypass_quirk) {


Given that the other thread [1] with Jordan (why haven't you cc'd him?! --
adding him now) has identified the need for a callback to allocate the
context bank, why don't we use the same sort of idea here? If the impl
provides a CB allocator function, call it irrespective of the domain type.
If it allocates a domain even for an identity domain, then we can install
if with SCTLR.M clear.

Will

[1] https://lore.kernel.org/r/20200716151625.GA14526@jcrouse1-lnx.qualcomm.com
Jordan Crouse July 20, 2020, 3:51 p.m. | #2
On Mon, Jul 20, 2020 at 09:58:42AM +0100, Will Deacon wrote:
> On Thu, Jul 16, 2020 at 05:16:16PM -0700, Bjorn Andersson wrote:

> > Some firmware found on various Qualcomm platforms traps writes to S2CR

> > of type BYPASS and writes FAULT into the register. This prevents us from

> > marking the streams for the display controller as BYPASS to allow

> > continued scanout of the screen through the initialization of the ARM

> > SMMU.

> > 

> > This adds a Qualcomm specific cfg_probe function, which probes the

> > behavior of the S2CR registers and if found faulty enables the related

> > quirk. Based on this quirk context banks are allocated for IDENTITY

> > domains as well, but with ARM_SMMU_SCTLR_M omitted.

> > 

> > The result is valid stream mappings, without translation.

> > 

> > Tested-by: John Stultz <john.stultz@linaro.org>

> > Tested-by: Vinod Koul <vkoul@kernel.org>

> > Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>

> > ---

> > 

> > Changes since v1:

> > - Picked up tested-by

> > 

> >  drivers/iommu/arm-smmu-qcom.c | 21 +++++++++++++++++++++

> >  drivers/iommu/arm-smmu.c      | 14 ++++++++++++--

> >  drivers/iommu/arm-smmu.h      |  3 +++

> >  3 files changed, 36 insertions(+), 2 deletions(-)

> 

> [...]

> 

> > diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c

> > index fb85e716ae9a..5d5fe6741ed4 100644

> > --- a/drivers/iommu/arm-smmu.c

> > +++ b/drivers/iommu/arm-smmu.c

> > @@ -654,7 +654,9 @@ static void arm_smmu_write_context_bank(struct arm_smmu_device *smmu, int idx)

> >  

> >  	/* SCTLR */

> >  	reg = ARM_SMMU_SCTLR_CFIE | ARM_SMMU_SCTLR_CFRE | ARM_SMMU_SCTLR_AFE |

> > -	      ARM_SMMU_SCTLR_TRE | ARM_SMMU_SCTLR_M;

> > +	      ARM_SMMU_SCTLR_TRE;

> > +	if (cfg->m)

> > +		reg |= ARM_SMMU_SCTLR_M;

> >  	if (stage1)

> >  		reg |= ARM_SMMU_SCTLR_S1_ASIDPNE;

> >  	if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN))

> > @@ -678,7 +680,11 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,

> >  	if (smmu_domain->smmu)

> >  		goto out_unlock;

> >  

> > -	if (domain->type == IOMMU_DOMAIN_IDENTITY) {

> > +	/*

> > +	 * Nothing to do for IDENTITY domains,unless disabled context banks are

> > +	 * used to emulate bypass mappings on Qualcomm platforms.

> > +	 */

> > +	if (domain->type == IOMMU_DOMAIN_IDENTITY && !smmu->qcom_bypass_quirk) {

> 

> Given that the other thread [1] with Jordan (why haven't you cc'd him?! --

> adding him now) has identified the need for a callback to allocate the

> context bank, why don't we use the same sort of idea here? If the impl

> provides a CB allocator function, call it irrespective of the domain type.

> If it allocates a domain even for an identity domain, then we can install

> if with SCTLR.M clear.


Here is what I have so far for the context bank allocator.  I think its a good
start, but it still feels a bit half baked, so comments definitely welcome.

https://lists.linuxfoundation.org/pipermail/iommu/2020-July/046754.html
https://lists.linuxfoundation.org/pipermail/iommu/2020-July/046752.html

> Will

> 

> [1] https://lore.kernel.org/r/20200716151625.GA14526@jcrouse1-lnx.qualcomm.com


-- 
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project

Patch

diff --git a/drivers/iommu/arm-smmu-qcom.c b/drivers/iommu/arm-smmu-qcom.c
index be4318044f96..d95a5ee8c83c 100644
--- a/drivers/iommu/arm-smmu-qcom.c
+++ b/drivers/iommu/arm-smmu-qcom.c
@@ -23,6 +23,26 @@  static const struct of_device_id qcom_smmu_client_of_match[] __maybe_unused = {
 	{ }
 };
 
+static int qcom_smmu_cfg_probe(struct arm_smmu_device *smmu)
+{
+	unsigned int last_s2cr = ARM_SMMU_GR0_S2CR(smmu->num_mapping_groups - 1);
+	u32 reg;
+
+	/*
+	 * With some firmware writes to S2CR of type FAULT are ignored, and
+	 * writing BYPASS will end up as FAULT in the register. Perform a write
+	 * to S2CR to detect if this is the case with the current firmware.
+	 */
+	arm_smmu_gr0_write(smmu, last_s2cr, FIELD_PREP(ARM_SMMU_S2CR_TYPE, S2CR_TYPE_BYPASS) |
+					    FIELD_PREP(ARM_SMMU_S2CR_CBNDX, 0xff) |
+					    FIELD_PREP(ARM_SMMU_S2CR_PRIVCFG, S2CR_PRIVCFG_DEFAULT));
+	reg = arm_smmu_gr0_read(smmu, last_s2cr);
+	if (FIELD_GET(ARM_SMMU_S2CR_TYPE, reg) != S2CR_TYPE_BYPASS)
+		smmu->qcom_bypass_quirk = true;
+
+	return 0;
+}
+
 static int qcom_smmu_def_domain_type(struct device *dev)
 {
 	const struct of_device_id *match =
@@ -61,6 +81,7 @@  static int qcom_smmu500_reset(struct arm_smmu_device *smmu)
 }
 
 static const struct arm_smmu_impl qcom_smmu_impl = {
+	.cfg_probe = qcom_smmu_cfg_probe,
 	.def_domain_type = qcom_smmu_def_domain_type,
 	.reset = qcom_smmu500_reset,
 };
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index fb85e716ae9a..5d5fe6741ed4 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -654,7 +654,9 @@  static void arm_smmu_write_context_bank(struct arm_smmu_device *smmu, int idx)
 
 	/* SCTLR */
 	reg = ARM_SMMU_SCTLR_CFIE | ARM_SMMU_SCTLR_CFRE | ARM_SMMU_SCTLR_AFE |
-	      ARM_SMMU_SCTLR_TRE | ARM_SMMU_SCTLR_M;
+	      ARM_SMMU_SCTLR_TRE;
+	if (cfg->m)
+		reg |= ARM_SMMU_SCTLR_M;
 	if (stage1)
 		reg |= ARM_SMMU_SCTLR_S1_ASIDPNE;
 	if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN))
@@ -678,7 +680,11 @@  static int arm_smmu_init_domain_context(struct iommu_domain *domain,
 	if (smmu_domain->smmu)
 		goto out_unlock;
 
-	if (domain->type == IOMMU_DOMAIN_IDENTITY) {
+	/*
+	 * Nothing to do for IDENTITY domains,unless disabled context banks are
+	 * used to emulate bypass mappings on Qualcomm platforms.
+	 */
+	if (domain->type == IOMMU_DOMAIN_IDENTITY && !smmu->qcom_bypass_quirk) {
 		smmu_domain->stage = ARM_SMMU_DOMAIN_BYPASS;
 		smmu_domain->smmu = smmu;
 		goto out_unlock;
@@ -826,6 +832,10 @@  static int arm_smmu_init_domain_context(struct iommu_domain *domain,
 	domain->geometry.aperture_end = (1UL << ias) - 1;
 	domain->geometry.force_aperture = true;
 
+	/* Enable translation for non-identity context banks */
+	if (domain->type != IOMMU_DOMAIN_IDENTITY)
+		cfg->m = true;
+
 	/* Initialise the context bank with our page table cfg */
 	arm_smmu_init_context_bank(smmu_domain, &pgtbl_cfg);
 	arm_smmu_write_context_bank(smmu, cfg->cbndx);
diff --git a/drivers/iommu/arm-smmu.h b/drivers/iommu/arm-smmu.h
index d172c024be61..a71d193073e4 100644
--- a/drivers/iommu/arm-smmu.h
+++ b/drivers/iommu/arm-smmu.h
@@ -305,6 +305,8 @@  struct arm_smmu_device {
 
 	/* IOMMU core code handle */
 	struct iommu_device		iommu;
+
+	bool				qcom_bypass_quirk;
 };
 
 enum arm_smmu_context_fmt {
@@ -323,6 +325,7 @@  struct arm_smmu_cfg {
 	};
 	enum arm_smmu_cbar_type		cbar;
 	enum arm_smmu_context_fmt	fmt;
+	bool				m;
 };
 #define ARM_SMMU_INVALID_IRPTNDX	0xff