diff mbox

[4/6] iommu/tegra: smmu: Support variable MMIO range

Message ID 1358237848-968-4-git-send-email-hdoyu@nvidia.com
State Not Applicable, archived
Headers show

Commit Message

Hiroshi Doyu Jan. 15, 2013, 8:17 a.m. UTC
There are 3 SMMU MMIO register blocks. They may get bigger as new
Tegra SoC comes. This patch enables to support variable size of those
register blocks.

Signed-off-by: Hiroshi Doyu <hdoyu@nvidia.com>
---
 drivers/iommu/tegra-smmu.c |   14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

Comments

Stephen Warren Jan. 16, 2013, 9:12 p.m. UTC | #1
On 01/15/2013 01:17 AM, Hiroshi Doyu wrote:
> There are 3 SMMU MMIO register blocks. They may get bigger as new
> Tegra SoC comes. This patch enables to support variable size of those
> register blocks.

Why would the register blocks move around? In the HW, there's one single
chunk of memory containing all the SMMU registers, and we simply carve
out a few holes since some unrelated registers are stuck in the middle,
thus leaving us with 3 register ranges. If the size of those carved out
chunks changes, then doesn't that mean all the registers moved around
within the single chunk, and hence all the register offsets in the
driver become invalid?

It may help if you provide an explicit example of what the register
layout change is...

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

>  static inline u32 smmu_read(struct smmu_device *smmu, size_t offs)
>  {
>  	BUG_ON(offs < 0x10);
> -	if (offs < 0x3c)
> +	if (offs < 0x10 + smmu->regsz[0])
>  		return readl(smmu->regs[0] + offs - 0x10);
>  	BUG_ON(offs < 0x1f0);
> -	if (offs < 0x200)
> +	if (offs < 0x1f0 + smmu->regsz[1])

Wouldn't you need to adjust that BUG_ON() in a similar way to how the if
condition was adjusted?
--
To unsubscribe from this list: send the line "unsubscribe linux-tegra" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Hiroshi Doyu Jan. 18, 2013, 9:05 a.m. UTC | #2
Stephen Warren <swarren@wwwdotorg.org> wrote @ Wed, 16 Jan 2013 22:12:24 +0100:

> On 01/15/2013 01:17 AM, Hiroshi Doyu wrote:
> > There are 3 SMMU MMIO register blocks. They may get bigger as new
> > Tegra SoC comes. This patch enables to support variable size of those
> > register blocks.
>
> Why would the register blocks move around? In the HW, there's one single
> chunk of memory containing all the SMMU registers, and we simply carve
> out a few holes since some unrelated registers are stuck in the middle,
> thus leaving us with 3 register ranges. If the size of those carved out
> chunks changes, then doesn't that mean all the registers moved around
> within the single chunk, and hence all the register offsets in the
> driver become invalid?

Presently there are 3 register blocks. In the future chips over some
generations, some of register block "size" can be extended to the end
and also more new register blocks will be added, which is expected at
most a few blocks. Usually the starting address of each block won't
change. Ideally SMMU register blocks should be in one block, but it
was a bit too late to change this design(or H/W). Considering this
situation, in this driver, number of register blocks should be
allocated dynamically, based on the info from DT. Its range checks
should be done in the accessors as below(*1) if necessary. This way
may sacrifice some perf because a new accessor prevents compiler
optimization of register offset calculation, but I think that SMMU
register accesses are not so frequent and it's acceptable in order to
unify "tegra-smmu" over Tegra SoCs.

*1:

 /*
  *	SMMU register accessors
  */
 static inline u32 smmu_read(struct smmu_device *smmu, size_t offs)
 {
 	void __iommu *addr = smmu->regbase + offs;
 #ifdef DEBUG
 	int i;

 	for (i = 0; i < smmu->num_regblks; i++) {
 		BUG_ON(addr < smmu->reg[i].start);
 		if (addr <= smmu->reg[i].end)
 			break;
 	}
 #endfi
 	return readl(addr);
 }

Even the checks if "offs" is in some of register blocks could be
ifdef'ed out with DEBUG. "smmu->regbase" can be calculated in probe()
as below. I don't think that we don't need to access "mc" DT entry to
get this address. since "smmu"'s 1st reg block always starts at 0x10.

  /* same as "mc"'s 1st reg block */
  smmu->regbase = smmu->reg[0] & PAGE_MASK;
--
To unsubscribe from this list: send the line "unsubscribe linux-tegra" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Stephen Warren Jan. 18, 2013, 4:44 p.m. UTC | #3
On 01/18/2013 02:05 AM, Hiroshi Doyu wrote:
> Stephen Warren <swarren@wwwdotorg.org> wrote @ Wed, 16 Jan 2013 22:12:24 +0100:
> 
>> On 01/15/2013 01:17 AM, Hiroshi Doyu wrote:
>>> There are 3 SMMU MMIO register blocks. They may get bigger as new
>>> Tegra SoC comes. This patch enables to support variable size of those
>>> register blocks.
>>
>> Why would the register blocks move around? In the HW, there's one single
>> chunk of memory containing all the SMMU registers, and we simply carve
>> out a few holes since some unrelated registers are stuck in the middle,
>> thus leaving us with 3 register ranges. If the size of those carved out
>> chunks changes, then doesn't that mean all the registers moved around
>> within the single chunk, and hence all the register offsets in the
>> driver become invalid?
> 
> Presently there are 3 register blocks. In the future chips over some
> generations, some of register block "size" can be extended to the end
> and also more new register blocks will be added, which is expected at
> most a few blocks.

> Usually the starting address of each block won't change.

I would hope that was guaranteed; the only reason to move one of the
ranges would be a HW design change, and if there is a HW design change,
HW should take that opportunity to fix the interleaved register mess
instead of making it worse, and hence we could just have a single
register range after that point.

> Ideally SMMU register blocks should be in one block, but it
> was a bit too late to change this design(or H/W). Considering this
> situation, in this driver, number of register blocks should be
> allocated dynamically, based on the info from DT. Its range checks
> should be done in the accessors as below(*1) if necessary. This way
> may sacrifice some perf because a new accessor prevents compiler
> optimization of register offset calculation, but I think that SMMU
> register accesses are not so frequent and it's acceptable in order to
> unify "tegra-smmu" over Tegra SoCs.
> 
> *1:
> 
>  /*
>   *	SMMU register accessors
>   */
>  static inline u32 smmu_read(struct smmu_device *smmu, size_t offs)
>  {
>  	void __iommu *addr = smmu->regbase + offs;
>  #ifdef DEBUG
>  	int i;
> 
>  	for (i = 0; i < smmu->num_regblks; i++) {
>  		BUG_ON(addr < smmu->reg[i].start);
>  		if (addr <= smmu->reg[i].end)
>  			break;
>  	}
>  #endfi
>  	return readl(addr);
>  }

Yes, that kind of checking looks much better if this is all going to be
dynamic.

I would suggest enabling the checking all the time rather than hiding it
inside an ifdef; how many people build that file with DEBUG enabled?

Or perhaps you could check each register in probe() somehow.

> Even the checks if "offs" is in some of register blocks could be
> ifdef'ed out with DEBUG. "smmu->regbase" can be calculated in probe()
> as below. I don't think that we don't need to access "mc" DT entry to
> get this address. since "smmu"'s 1st reg block always starts at 0x10.
> 
>   /* same as "mc"'s 1st reg block */
>   smmu->regbase = smmu->reg[0] & PAGE_MASK;

I don't see regbase in the existing driver or your patch. Are you
proposing to simply make readl/writel add the offset onto a base address
that's calculated like that? That may not work in general; if the SMMU
register ranges cross a page boundary, and the various separate ranges
end up getting mapped to non-contiguous virtual addresses, using a
single base address won't work.
--
To unsubscribe from this list: send the line "unsubscribe linux-tegra" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c
index e80312c..4f3a2a5 100644
--- a/drivers/iommu/tegra-smmu.c
+++ b/drivers/iommu/tegra-smmu.c
@@ -222,6 +222,7 @@  struct smmu_debugfs_info {
  */
 struct smmu_device {
 	void __iomem	*regs[NUM_SMMU_REG_BANKS];
+	resource_size_t	regsz[NUM_SMMU_REG_BANKS];
 	unsigned long	iovmm_base;	/* remappable base address */
 	unsigned long	page_count;	/* total remappable size */
 	spinlock_t	lock;
@@ -257,13 +258,13 @@  static struct smmu_device *smmu_handle; /* unique for a system */
 static inline u32 smmu_read(struct smmu_device *smmu, size_t offs)
 {
 	BUG_ON(offs < 0x10);
-	if (offs < 0x3c)
+	if (offs < 0x10 + smmu->regsz[0])
 		return readl(smmu->regs[0] + offs - 0x10);
 	BUG_ON(offs < 0x1f0);
-	if (offs < 0x200)
+	if (offs < 0x1f0 + smmu->regsz[1])
 		return readl(smmu->regs[1] + offs - 0x1f0);
 	BUG_ON(offs < 0x228);
-	if (offs < 0x284)
+	if (offs < 0x228 + smmu->regsz[2])
 		return readl(smmu->regs[2] + offs - 0x228);
 	BUG();
 }
@@ -271,17 +272,17 @@  static inline u32 smmu_read(struct smmu_device *smmu, size_t offs)
 static inline void smmu_write(struct smmu_device *smmu, u32 val, size_t offs)
 {
 	BUG_ON(offs < 0x10);
-	if (offs < 0x3c) {
+	if (offs < 0x10 + smmu->regsz[0]) {
 		writel(val, smmu->regs[0] + offs - 0x10);
 		return;
 	}
 	BUG_ON(offs < 0x1f0);
-	if (offs < 0x200) {
+	if (offs < 0x1f0 + smmu->regsz[1]) {
 		writel(val, smmu->regs[1] + offs - 0x1f0);
 		return;
 	}
 	BUG_ON(offs < 0x228);
-	if (offs < 0x284) {
+	if (offs < 0x228 + smmu->regsz[2]) {
 		writel(val, smmu->regs[2] + offs - 0x228);
 		return;
 	}
@@ -1117,6 +1118,7 @@  static int tegra_smmu_probe(struct platform_device *pdev)
 		smmu->regs[i] = devm_request_and_ioremap(&pdev->dev, res);
 		if (!smmu->regs[i])
 			return -EBUSY;
+		smmu->regsz[i] = resource_size(res);
 	}
 
 	err = of_get_dma_window(dev->of_node, NULL, 0, NULL, &base, &size);