diff options
author | Hiroshi Doyu <hdoyu@nvidia.com> | 2013-01-31 03:14:10 -0500 |
---|---|---|
committer | Joerg Roedel <joro@8bytes.org> | 2013-02-05 08:18:01 -0500 |
commit | a6870e928d1b2a97d95e7bf1aaefd3da34b83a85 (patch) | |
tree | edb13665e5cbfe256fd5dc52bdfc55d15ec26839 /drivers/iommu/tegra-smmu.c | |
parent | 0fde671b81d406a59ae21f35f121851509c3b138 (diff) |
iommu/tegra: smmu: Support variable MMIO ranges/blocks
Presently SMMU registers are located in discontiguous 3 blocks. They
are interleaved by MC registers. Ideally SMMU register blocks should
be in an independent one block, but it is too late to change this H/W
design. In the future Tegra chips over some generations, it is
expected that some of register block "size" can be extended towards
the end and also more new register blocks will be added at most a few
blocks. The starting address of each existing block won't change. This
patch allocates multiple number of register blocks dynamically based
on the info passed from DT. Those ranges are verified in the
accessors{read,write}. This may sacrifice some performance because a
new accessors prevents compiler optimization of a fixed size register
offset calculation. Since SMMU register accesses are not so frequent,
this would be acceptable. This patch is necessary to unify
"tegra-smmu.ko" over some Tegra SoC generations.
Signed-off-by: Hiroshi Doyu <hdoyu@nvidia.com>
Reviewed-by: Stephen Warren <swarren@nvidia.com>
Signed-off-by: Joerg Roedel <joro@8bytes.org>
Diffstat (limited to 'drivers/iommu/tegra-smmu.c')
-rw-r--r-- | drivers/iommu/tegra-smmu.c | 61 |
1 files changed, 36 insertions, 25 deletions
diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c index 87a719f4adfb..2fecbe7fd7fd 100644 --- a/drivers/iommu/tegra-smmu.c +++ b/drivers/iommu/tegra-smmu.c | |||
@@ -293,7 +293,11 @@ struct smmu_debugfs_info { | |||
293 | * Per SMMU device - IOMMU device | 293 | * Per SMMU device - IOMMU device |
294 | */ | 294 | */ |
295 | struct smmu_device { | 295 | struct smmu_device { |
296 | void __iomem *regs[NUM_SMMU_REG_BANKS]; | 296 | void __iomem *regbase; /* register offset base */ |
297 | void __iomem **regs; /* register block start address array */ | ||
298 | void __iomem **rege; /* register block end address array */ | ||
299 | int nregs; /* number of register blocks */ | ||
300 | |||
297 | unsigned long iovmm_base; /* remappable base address */ | 301 | unsigned long iovmm_base; /* remappable base address */ |
298 | unsigned long page_count; /* total remappable size */ | 302 | unsigned long page_count; /* total remappable size */ |
299 | spinlock_t lock; | 303 | spinlock_t lock; |
@@ -325,35 +329,33 @@ static struct smmu_device *smmu_handle; /* unique for a system */ | |||
325 | */ | 329 | */ |
326 | static inline u32 smmu_read(struct smmu_device *smmu, size_t offs) | 330 | static inline u32 smmu_read(struct smmu_device *smmu, size_t offs) |
327 | { | 331 | { |
328 | BUG_ON(offs < 0x10); | 332 | int i; |
329 | if (offs < 0x3c) | 333 | |
330 | return readl(smmu->regs[0] + offs - 0x10); | 334 | for (i = 0; i < smmu->nregs; i++) { |
331 | BUG_ON(offs < 0x1f0); | 335 | void __iomem *addr = smmu->regbase + offs; |
332 | if (offs < 0x200) | 336 | |
333 | return readl(smmu->regs[1] + offs - 0x1f0); | 337 | BUG_ON(addr < smmu->regs[i]); |
334 | BUG_ON(offs < 0x228); | 338 | if (addr <= smmu->rege[i]) |
335 | if (offs < 0x284) | 339 | return readl(addr); |
336 | return readl(smmu->regs[2] + offs - 0x228); | 340 | } |
341 | |||
337 | BUG(); | 342 | BUG(); |
338 | } | 343 | } |
339 | 344 | ||
340 | static inline void smmu_write(struct smmu_device *smmu, u32 val, size_t offs) | 345 | static inline void smmu_write(struct smmu_device *smmu, u32 val, size_t offs) |
341 | { | 346 | { |
342 | BUG_ON(offs < 0x10); | 347 | int i; |
343 | if (offs < 0x3c) { | 348 | |
344 | writel(val, smmu->regs[0] + offs - 0x10); | 349 | for (i = 0; i < smmu->nregs; i++) { |
345 | return; | 350 | void __iomem *addr = smmu->regbase + offs; |
346 | } | 351 | |
347 | BUG_ON(offs < 0x1f0); | 352 | BUG_ON(addr < smmu->regs[i]); |
348 | if (offs < 0x200) { | 353 | if (addr <= smmu->rege[i]) { |
349 | writel(val, smmu->regs[1] + offs - 0x1f0); | 354 | writel(val, addr); |
350 | return; | 355 | return; |
351 | } | 356 | } |
352 | BUG_ON(offs < 0x228); | ||
353 | if (offs < 0x284) { | ||
354 | writel(val, smmu->regs[2] + offs - 0x228); | ||
355 | return; | ||
356 | } | 357 | } |
358 | |||
357 | BUG(); | 359 | BUG(); |
358 | } | 360 | } |
359 | 361 | ||
@@ -1170,7 +1172,13 @@ static int tegra_smmu_probe(struct platform_device *pdev) | |||
1170 | return -ENOMEM; | 1172 | return -ENOMEM; |
1171 | } | 1173 | } |
1172 | 1174 | ||
1173 | for (i = 0; i < ARRAY_SIZE(smmu->regs); i++) { | 1175 | smmu->nregs = pdev->num_resources; |
1176 | smmu->regs = devm_kzalloc(dev, 2 * smmu->nregs * sizeof(*smmu->regs), | ||
1177 | GFP_KERNEL); | ||
1178 | smmu->rege = smmu->regs + smmu->nregs; | ||
1179 | if (!smmu->regs) | ||
1180 | return -ENOMEM; | ||
1181 | for (i = 0; i < smmu->nregs; i++) { | ||
1174 | struct resource *res; | 1182 | struct resource *res; |
1175 | 1183 | ||
1176 | res = platform_get_resource(pdev, IORESOURCE_MEM, i); | 1184 | res = platform_get_resource(pdev, IORESOURCE_MEM, i); |
@@ -1179,7 +1187,10 @@ static int tegra_smmu_probe(struct platform_device *pdev) | |||
1179 | smmu->regs[i] = devm_request_and_ioremap(&pdev->dev, res); | 1187 | smmu->regs[i] = devm_request_and_ioremap(&pdev->dev, res); |
1180 | if (!smmu->regs[i]) | 1188 | if (!smmu->regs[i]) |
1181 | return -EBUSY; | 1189 | return -EBUSY; |
1190 | smmu->rege[i] = smmu->regs[i] + resource_size(res) - 1; | ||
1182 | } | 1191 | } |
1192 | /* Same as "mc" 1st regiter block start address */ | ||
1193 | smmu->regbase = (void __iomem *)((u32)smmu->regs[0] & ~PAGE_MASK); | ||
1183 | 1194 | ||
1184 | err = of_get_dma_window(dev->of_node, NULL, 0, NULL, &base, &size); | 1195 | err = of_get_dma_window(dev->of_node, NULL, 0, NULL, &base, &size); |
1185 | if (err) | 1196 | if (err) |