[PATCH 0/9] arm64: tegra: Prevent early SMMU faults
Dmitry Osipenko
digetx at gmail.com
Fri Mar 26 15:29:28 GMT 2021
25.03.2021 16:03, Thierry Reding пишет:
> From: Thierry Reding <treding at nvidia.com>
>
> Hi,
>
> this is a set of patches that is the result of earlier discussions
> regarding early identity mappings that are needed to avoid SMMU faults
> during early boot.
>
> The goal here is to avoid early identity mappings altogether and instead
> postpone the need for the identity mappings to when devices are attached
> to the SMMU. This works by making the SMMU driver coordinate with the
> memory controller driver on when to start enforcing SMMU translations.
> This makes Tegra behave in a more standard way and pushes the code to
> deal with the Tegra-specific programming into the NVIDIA SMMU
> implementation.
It is an interesting idea which inspired me to try to apply a somewhat similar thing to Tegra SMMU driver by holding the SMMU ASID enable-bit until display driver allows to toggle it. This means that we will need an extra small tegra-specific SMMU API function, but it should be okay.
I typed a patch and seems it's working good, I'll prepare a proper patch if you like it.
What do you think about this:
diff --git a/drivers/gpu/drm/grate/dc.c b/drivers/gpu/drm/grate/dc.c
index 45a41586f153..8874cfba40a1 100644
--- a/drivers/gpu/drm/grate/dc.c
+++ b/drivers/gpu/drm/grate/dc.c
@@ -17,6 +17,7 @@
#include <linux/reset.h>
#include <soc/tegra/common.h>
+#include <soc/tegra/mc.h>
#include <soc/tegra/pmc.h>
#include <drm/drm_atomic.h>
@@ -2640,6 +2641,11 @@ static int tegra_dc_init(struct host1x_client *client)
return err;
}
+ if (dc->soc->sync_smmu) {
+ struct iommu_domain *domain = iommu_get_domain_for_dev(dc->dev);
+ tegra_smmu_sync_domain(domain, dc->dev);
+ }
+
if (dc->soc->wgrps)
primary = tegra_dc_add_shared_planes(drm, dc);
else
@@ -2824,6 +2830,7 @@ static const struct tegra_dc_soc_info tegra20_dc_soc_info = {
.has_win_b_vfilter_mem_client = true,
.has_win_c_without_vert_filter = true,
.plane_tiled_memory_bandwidth_x2 = false,
+ .sync_smmu = false,
};
static const struct tegra_dc_soc_info tegra30_dc_soc_info = {
@@ -2846,6 +2853,7 @@ static const struct tegra_dc_soc_info tegra30_dc_soc_info = {
.has_win_b_vfilter_mem_client = true,
.has_win_c_without_vert_filter = false,
.plane_tiled_memory_bandwidth_x2 = true,
+ .sync_smmu = true,
};
static const struct tegra_dc_soc_info tegra114_dc_soc_info = {
@@ -2868,6 +2876,7 @@ static const struct tegra_dc_soc_info tegra114_dc_soc_info = {
.has_win_b_vfilter_mem_client = false,
.has_win_c_without_vert_filter = false,
.plane_tiled_memory_bandwidth_x2 = true,
+ .sync_smmu = true,
};
static const struct tegra_dc_soc_info tegra124_dc_soc_info = {
@@ -2890,6 +2899,7 @@ static const struct tegra_dc_soc_info tegra124_dc_soc_info = {
.has_win_b_vfilter_mem_client = false,
.has_win_c_without_vert_filter = false,
.plane_tiled_memory_bandwidth_x2 = false,
+ .sync_smmu = true,
};
static const struct tegra_dc_soc_info tegra210_dc_soc_info = {
@@ -2912,6 +2922,7 @@ static const struct tegra_dc_soc_info tegra210_dc_soc_info = {
.has_win_b_vfilter_mem_client = false,
.has_win_c_without_vert_filter = false,
.plane_tiled_memory_bandwidth_x2 = false,
+ .sync_smmu = true,
};
static const struct tegra_windowgroup_soc tegra186_dc_wgrps[] = {
@@ -2961,6 +2972,7 @@ static const struct tegra_dc_soc_info tegra186_dc_soc_info = {
.wgrps = tegra186_dc_wgrps,
.num_wgrps = ARRAY_SIZE(tegra186_dc_wgrps),
.plane_tiled_memory_bandwidth_x2 = false,
+ .sync_smmu = false,
};
static const struct tegra_windowgroup_soc tegra194_dc_wgrps[] = {
@@ -3010,6 +3022,7 @@ static const struct tegra_dc_soc_info tegra194_dc_soc_info = {
.wgrps = tegra194_dc_wgrps,
.num_wgrps = ARRAY_SIZE(tegra194_dc_wgrps),
.plane_tiled_memory_bandwidth_x2 = false,
+ .sync_smmu = false,
};
static const struct of_device_id tegra_dc_of_match[] = {
diff --git a/drivers/gpu/drm/grate/dc.h b/drivers/gpu/drm/grate/dc.h
index 316a56131cf1..e0057bf7be99 100644
--- a/drivers/gpu/drm/grate/dc.h
+++ b/drivers/gpu/drm/grate/dc.h
@@ -91,6 +91,7 @@ struct tegra_dc_soc_info {
bool has_win_b_vfilter_mem_client;
bool has_win_c_without_vert_filter;
bool plane_tiled_memory_bandwidth_x2;
+ bool sync_smmu;
};
struct tegra_dc {
diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c
index 602aab98c079..e750b1844a88 100644
--- a/drivers/iommu/tegra-smmu.c
+++ b/drivers/iommu/tegra-smmu.c
@@ -47,6 +47,9 @@ struct tegra_smmu {
struct dentry *debugfs;
struct iommu_device iommu; /* IOMMU Core code handle */
+
+ bool display_synced[2];
+ bool display_enabled[2];
};
struct tegra_smmu_as {
@@ -78,6 +81,10 @@ static inline u32 smmu_readl(struct tegra_smmu *smmu, unsigned long offset)
return readl(smmu->regs + offset);
}
+/* all Tegra SoCs use the same group IDs for displays */
+#define SMMU_SWGROUP_DC 1
+#define SMMU_SWGROUP_DCB 2
+
#define SMMU_CONFIG 0x010
#define SMMU_CONFIG_ENABLE (1 << 0)
@@ -253,6 +260,20 @@ static inline void smmu_flush(struct tegra_smmu *smmu)
smmu_readl(smmu, SMMU_PTB_ASID);
}
+static int smmu_swgroup_to_display_id(unsigned int swgroup)
+{
+ switch (swgroup) {
+ case SMMU_SWGROUP_DC:
+ return 0;
+
+ case SMMU_SWGROUP_DCB:
+ return 1;
+
+ default:
+ return -1;
+ }
+}
+
static int tegra_smmu_alloc_asid(struct tegra_smmu *smmu, unsigned int *idp)
{
unsigned long id;
@@ -352,10 +373,21 @@ tegra_smmu_find_swgroup(struct tegra_smmu *smmu, unsigned int swgroup)
static void tegra_smmu_enable(struct tegra_smmu *smmu, unsigned int swgroup,
unsigned int asid)
{
+ const int disp_id = smmu_swgroup_to_display_id(swgroup);
const struct tegra_smmu_swgroup *group;
unsigned int i;
u32 value;
+ if (disp_id >= 0) {
+ smmu->display_enabled[disp_id] = true;
+
+ if (!smmu->display_synced[disp_id]) {
+ pr_debug("%s deferred for swgroup %u\n",
+ __func__, swgroup);
+ return;
+ }
+ }
+
group = tegra_smmu_find_swgroup(smmu, swgroup);
if (group) {
value = smmu_readl(smmu, group->reg);
@@ -385,10 +417,14 @@ static void tegra_smmu_enable(struct tegra_smmu *smmu, unsigned int swgroup,
static void tegra_smmu_disable(struct tegra_smmu *smmu, unsigned int swgroup,
unsigned int asid)
{
+ const int disp_id = smmu_swgroup_to_display_id(swgroup);
const struct tegra_smmu_swgroup *group;
unsigned int i;
u32 value;
+ if (disp_id >= 0)
+ smmu->display_enabled[disp_id] = false;
+
group = tegra_smmu_find_swgroup(smmu, swgroup);
if (group) {
value = smmu_readl(smmu, group->reg);
@@ -410,6 +446,32 @@ static void tegra_smmu_disable(struct tegra_smmu *smmu, unsigned int swgroup,
}
}
+void tegra_smmu_sync_domain(struct iommu_domain *domain, struct device *dev)
+{
+ struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
+ struct tegra_smmu *smmu = dev_iommu_priv_get(dev);
+ unsigned int index;
+
+ if (!fwspec || !domain)
+ return;
+
+ for (index = 0; index < fwspec->num_ids; index++) {
+ const unsigned int swgroup = fwspec->ids[index];
+ const int disp_id = smmu_swgroup_to_display_id(swgroup);
+
+ if (disp_id < 0 || smmu->display_synced[disp_id])
+ continue;
+
+ smmu->display_synced[disp_id] = true;
+
+ if (!smmu->display_enabled[disp_id])
+ continue;
+
+ tegra_smmu_enable(smmu, swgroup, to_smmu_as(domain)->id);
+ }
+}
+EXPORT_SYMBOL_GPL(tegra_smmu_sync_domain);
+
static int tegra_smmu_as_prepare(struct tegra_smmu *smmu,
struct tegra_smmu_as *as)
{
diff --git a/include/soc/tegra/mc.h b/include/soc/tegra/mc.h
index cfd3b35e23e5..ac1f3226b2ac 100644
--- a/include/soc/tegra/mc.h
+++ b/include/soc/tegra/mc.h
@@ -15,6 +15,7 @@
struct clk;
struct device;
+struct iommu_domain;
struct page;
struct tegra_smmu_enable {
@@ -88,6 +89,7 @@ struct tegra_smmu *tegra_smmu_probe(struct device *dev,
const struct tegra_smmu_soc *soc,
struct tegra_mc *mc);
void tegra_smmu_remove(struct tegra_smmu *smmu);
+void tegra_smmu_sync_domain(struct iommu_domain *domain, struct device *dev);
#else
static inline struct tegra_smmu *
tegra_smmu_probe(struct device *dev, const struct tegra_smmu_soc *soc,
@@ -99,6 +101,11 @@ tegra_smmu_probe(struct device *dev, const struct tegra_smmu_soc *soc,
static inline void tegra_smmu_remove(struct tegra_smmu *smmu)
{
}
+
+static inline void tegra_smmu_sync_domain(struct iommu_domain *domain,
+ struct device *dev)
+{
+}
#endif
#ifdef CONFIG_TEGRA_IOMMU_GART
More information about the linux-arm-kernel
mailing list