Skip to content

Commit 765a9d1

Browse files
Nicolin Chenjoergroedel
authored andcommitted
iommu/tegra-smmu: Fix mc errors on tegra124-nyan
Commit 25938c7 ("iommu/tegra-smmu: Rework tegra_smmu_probe_device()") removed certain hack in the tegra_smmu_probe() by relying on IOMMU core to of_xlate SMMU's SID per device, so as to get rid of tegra_smmu_find() and tegra_smmu_configure() that are typically done in the IOMMU core also. This approach works for both existing devices that have DT nodes and other devices (like PCI device) that don't exist in DT, on Tegra210 and Tegra3 upon testing. However, Page Fault errors are reported on tegra124-Nyan: tegra-mc 70019000.memory-controller: display0a: read @0xfe056b40: EMEM address decode error (SMMU translation error [--S]) tegra-mc 70019000.memory-controller: display0a: read @0xfe056b40: Page fault (SMMU translation error [--S]) After debugging, I found that the mentioned commit changed some function callback sequence of tegra-smmu's, resulting in enabling SMMU for display client before display driver gets initialized. I couldn't reproduce exact same issue on Tegra210 as Tegra124 (arm-32) differs at arch-level code. Actually this Page Fault is a known issue, as on most of Tegra platforms, display gets enabled by the bootloader for the splash screen feature, so it keeps filling the framebuffer memory. A proper fix to this issue is to 1:1 linear map the framebuffer memory to IOVA space so the SMMU will have the same address as the physical address in its page table. Yet, Thierry has been working on the solution above for a year, and it hasn't merged. Therefore, let's partially revert the mentioned commit to fix the errors. The reason why we do a partial revert here is that we can still set priv in ->of_xlate() callback for PCI devices. Meanwhile, devices existing in DT, like display, will go through tegra_smmu_configure() at the stage of bus_set_iommu() when SMMU gets probed(), as what it did before we merged the mentioned commit. Once we have the linear map solution for framebuffer memory, this change can be cleaned away. [Big thank to Guillaume who reported and helped debugging/verification] Fixes: 25938c7 ("iommu/tegra-smmu: Rework tegra_smmu_probe_device()") Reported-by: Guillaume Tucker <guillaume.tucker@collabora.com> Signed-off-by: Nicolin Chen <nicoleotsuka@gmail.com> Tested-by: Guillaume Tucker <guillaume.tucker@collabora.com> Acked-by: Thierry Reding <treding@nvidia.com> Link: https://lore.kernel.org/r/20210218220702.1962-1-nicoleotsuka@gmail.com Signed-off-by: Joerg Roedel <jroedel@suse.de>
1 parent 140456f commit 765a9d1

1 file changed

Lines changed: 71 additions & 1 deletion

File tree

drivers/iommu/tegra-smmu.c

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -798,10 +798,70 @@ static phys_addr_t tegra_smmu_iova_to_phys(struct iommu_domain *domain,
798798
return SMMU_PFN_PHYS(pfn) + SMMU_OFFSET_IN_PAGE(iova);
799799
}
800800

801+
static struct tegra_smmu *tegra_smmu_find(struct device_node *np)
802+
{
803+
struct platform_device *pdev;
804+
struct tegra_mc *mc;
805+
806+
pdev = of_find_device_by_node(np);
807+
if (!pdev)
808+
return NULL;
809+
810+
mc = platform_get_drvdata(pdev);
811+
if (!mc)
812+
return NULL;
813+
814+
return mc->smmu;
815+
}
816+
817+
static int tegra_smmu_configure(struct tegra_smmu *smmu, struct device *dev,
818+
struct of_phandle_args *args)
819+
{
820+
const struct iommu_ops *ops = smmu->iommu.ops;
821+
int err;
822+
823+
err = iommu_fwspec_init(dev, &dev->of_node->fwnode, ops);
824+
if (err < 0) {
825+
dev_err(dev, "failed to initialize fwspec: %d\n", err);
826+
return err;
827+
}
828+
829+
err = ops->of_xlate(dev, args);
830+
if (err < 0) {
831+
dev_err(dev, "failed to parse SW group ID: %d\n", err);
832+
iommu_fwspec_free(dev);
833+
return err;
834+
}
835+
836+
return 0;
837+
}
838+
801839
static struct iommu_device *tegra_smmu_probe_device(struct device *dev)
802840
{
803-
struct tegra_smmu *smmu = dev_iommu_priv_get(dev);
841+
struct device_node *np = dev->of_node;
842+
struct tegra_smmu *smmu = NULL;
843+
struct of_phandle_args args;
844+
unsigned int index = 0;
845+
int err;
846+
847+
while (of_parse_phandle_with_args(np, "iommus", "#iommu-cells", index,
848+
&args) == 0) {
849+
smmu = tegra_smmu_find(args.np);
850+
if (smmu) {
851+
err = tegra_smmu_configure(smmu, dev, &args);
852+
of_node_put(args.np);
804853

854+
if (err < 0)
855+
return ERR_PTR(err);
856+
857+
break;
858+
}
859+
860+
of_node_put(args.np);
861+
index++;
862+
}
863+
864+
smmu = dev_iommu_priv_get(dev);
805865
if (!smmu)
806866
return ERR_PTR(-ENODEV);
807867

@@ -1028,6 +1088,16 @@ struct tegra_smmu *tegra_smmu_probe(struct device *dev,
10281088
if (!smmu)
10291089
return ERR_PTR(-ENOMEM);
10301090

1091+
/*
1092+
* This is a bit of a hack. Ideally we'd want to simply return this
1093+
* value. However the IOMMU registration process will attempt to add
1094+
* all devices to the IOMMU when bus_set_iommu() is called. In order
1095+
* not to rely on global variables to track the IOMMU instance, we
1096+
* set it here so that it can be looked up from the .probe_device()
1097+
* callback via the IOMMU device's .drvdata field.
1098+
*/
1099+
mc->smmu = smmu;
1100+
10311101
size = BITS_TO_LONGS(soc->num_asids) * sizeof(long);
10321102

10331103
smmu->asids = devm_kzalloc(dev, size, GFP_KERNEL);

0 commit comments

Comments
 (0)