diff options
author | Alexandre Courbot <acourbot@nvidia.com> | 2015-02-20 04:23:03 -0500 |
---|---|---|
committer | Ben Skeggs <bskeggs@redhat.com> | 2015-04-14 03:00:44 -0400 |
commit | 58fd9375c2c5344e8ab6ef9971635bc59cd39658 (patch) | |
tree | 85727259dfb9e87703762bcfc5f110c96c4b554c | |
parent | 5dc240bcfe9a8d30b151be58b174261ba388cb01 (diff) |
drm/nouveau/platform: probe IOMMU if present
Tegra SoCs have an IOMMU that can be used to present non-contiguous
physical memory as contiguous to the GPU and maximize the use of large
pages in the GPU MMU, leading to performance gains. This patch adds
support for probing such a IOMMU if present and make its properties
available in the nouveau_platform_gpu structure so subsystems can take
advantage of it.
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_platform.c | 75 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_platform.h | 18 |
2 files changed, 92 insertions, 1 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_platform.c b/drivers/gpu/drm/nouveau/nouveau_platform.c index dc5900bf54ff..3691982452a9 100644 --- a/drivers/gpu/drm/nouveau/nouveau_platform.c +++ b/drivers/gpu/drm/nouveau/nouveau_platform.c | |||
@@ -27,6 +27,7 @@ | |||
27 | #include <linux/of.h> | 27 | #include <linux/of.h> |
28 | #include <linux/reset.h> | 28 | #include <linux/reset.h> |
29 | #include <linux/regulator/consumer.h> | 29 | #include <linux/regulator/consumer.h> |
30 | #include <linux/iommu.h> | ||
30 | #include <soc/tegra/fuse.h> | 31 | #include <soc/tegra/fuse.h> |
31 | #include <soc/tegra/pmc.h> | 32 | #include <soc/tegra/pmc.h> |
32 | 33 | ||
@@ -91,6 +92,71 @@ static int nouveau_platform_power_down(struct nouveau_platform_gpu *gpu) | |||
91 | return 0; | 92 | return 0; |
92 | } | 93 | } |
93 | 94 | ||
95 | static void nouveau_platform_probe_iommu(struct device *dev, | ||
96 | struct nouveau_platform_gpu *gpu) | ||
97 | { | ||
98 | int err; | ||
99 | unsigned long pgsize_bitmap; | ||
100 | |||
101 | mutex_init(&gpu->iommu.mutex); | ||
102 | |||
103 | if (iommu_present(&platform_bus_type)) { | ||
104 | gpu->iommu.domain = iommu_domain_alloc(&platform_bus_type); | ||
105 | if (IS_ERR(gpu->iommu.domain)) | ||
106 | goto error; | ||
107 | |||
108 | /* | ||
109 | * A IOMMU is only usable if it supports page sizes smaller | ||
110 | * or equal to the system's PAGE_SIZE, with a preference if | ||
111 | * both are equal. | ||
112 | */ | ||
113 | pgsize_bitmap = gpu->iommu.domain->ops->pgsize_bitmap; | ||
114 | if (pgsize_bitmap & PAGE_SIZE) { | ||
115 | gpu->iommu.pgshift = PAGE_SHIFT; | ||
116 | } else { | ||
117 | gpu->iommu.pgshift = fls(pgsize_bitmap & ~PAGE_MASK); | ||
118 | if (gpu->iommu.pgshift == 0) { | ||
119 | dev_warn(dev, "unsupported IOMMU page size\n"); | ||
120 | goto free_domain; | ||
121 | } | ||
122 | gpu->iommu.pgshift -= 1; | ||
123 | } | ||
124 | |||
125 | err = iommu_attach_device(gpu->iommu.domain, dev); | ||
126 | if (err) | ||
127 | goto free_domain; | ||
128 | |||
129 | err = nvkm_mm_init(&gpu->iommu._mm, 0, | ||
130 | (1ULL << 40) >> gpu->iommu.pgshift, 1); | ||
131 | if (err) | ||
132 | goto detach_device; | ||
133 | |||
134 | gpu->iommu.mm = &gpu->iommu._mm; | ||
135 | } | ||
136 | |||
137 | return; | ||
138 | |||
139 | detach_device: | ||
140 | iommu_detach_device(gpu->iommu.domain, dev); | ||
141 | |||
142 | free_domain: | ||
143 | iommu_domain_free(gpu->iommu.domain); | ||
144 | |||
145 | error: | ||
146 | gpu->iommu.domain = NULL; | ||
147 | gpu->iommu.pgshift = 0; | ||
148 | dev_err(dev, "cannot initialize IOMMU MM\n"); | ||
149 | } | ||
150 | |||
151 | static void nouveau_platform_remove_iommu(struct device *dev, | ||
152 | struct nouveau_platform_gpu *gpu) | ||
153 | { | ||
154 | if (gpu->iommu.domain) { | ||
155 | iommu_detach_device(gpu->iommu.domain, dev); | ||
156 | iommu_domain_free(gpu->iommu.domain); | ||
157 | } | ||
158 | } | ||
159 | |||
94 | static int nouveau_platform_probe(struct platform_device *pdev) | 160 | static int nouveau_platform_probe(struct platform_device *pdev) |
95 | { | 161 | { |
96 | struct nouveau_platform_gpu *gpu; | 162 | struct nouveau_platform_gpu *gpu; |
@@ -118,6 +184,8 @@ static int nouveau_platform_probe(struct platform_device *pdev) | |||
118 | if (IS_ERR(gpu->clk_pwr)) | 184 | if (IS_ERR(gpu->clk_pwr)) |
119 | return PTR_ERR(gpu->clk_pwr); | 185 | return PTR_ERR(gpu->clk_pwr); |
120 | 186 | ||
187 | nouveau_platform_probe_iommu(&pdev->dev, gpu); | ||
188 | |||
121 | err = nouveau_platform_power_up(gpu); | 189 | err = nouveau_platform_power_up(gpu); |
122 | if (err) | 190 | if (err) |
123 | return err; | 191 | return err; |
@@ -154,10 +222,15 @@ static int nouveau_platform_remove(struct platform_device *pdev) | |||
154 | struct nouveau_drm *drm = nouveau_drm(drm_dev); | 222 | struct nouveau_drm *drm = nouveau_drm(drm_dev); |
155 | struct nvkm_device *device = nvxx_device(&drm->device); | 223 | struct nvkm_device *device = nvxx_device(&drm->device); |
156 | struct nouveau_platform_gpu *gpu = nv_device_to_platform(device)->gpu; | 224 | struct nouveau_platform_gpu *gpu = nv_device_to_platform(device)->gpu; |
225 | int err; | ||
157 | 226 | ||
158 | nouveau_drm_device_remove(drm_dev); | 227 | nouveau_drm_device_remove(drm_dev); |
159 | 228 | ||
160 | return nouveau_platform_power_down(gpu); | 229 | err = nouveau_platform_power_down(gpu); |
230 | |||
231 | nouveau_platform_remove_iommu(&pdev->dev, gpu); | ||
232 | |||
233 | return err; | ||
161 | } | 234 | } |
162 | 235 | ||
163 | #if IS_ENABLED(CONFIG_OF) | 236 | #if IS_ENABLED(CONFIG_OF) |
diff --git a/drivers/gpu/drm/nouveau/nouveau_platform.h b/drivers/gpu/drm/nouveau/nouveau_platform.h index 268bb7213681..392874cf4725 100644 --- a/drivers/gpu/drm/nouveau/nouveau_platform.h +++ b/drivers/gpu/drm/nouveau/nouveau_platform.h | |||
@@ -24,10 +24,12 @@ | |||
24 | #define __NOUVEAU_PLATFORM_H__ | 24 | #define __NOUVEAU_PLATFORM_H__ |
25 | 25 | ||
26 | #include "core/device.h" | 26 | #include "core/device.h" |
27 | #include "core/mm.h" | ||
27 | 28 | ||
28 | struct reset_control; | 29 | struct reset_control; |
29 | struct clk; | 30 | struct clk; |
30 | struct regulator; | 31 | struct regulator; |
32 | struct iommu_domain; | ||
31 | struct platform_driver; | 33 | struct platform_driver; |
32 | 34 | ||
33 | struct nouveau_platform_gpu { | 35 | struct nouveau_platform_gpu { |
@@ -36,6 +38,22 @@ struct nouveau_platform_gpu { | |||
36 | struct clk *clk_pwr; | 38 | struct clk *clk_pwr; |
37 | 39 | ||
38 | struct regulator *vdd; | 40 | struct regulator *vdd; |
41 | |||
42 | struct { | ||
43 | /* | ||
44 | * Protects accesses to mm from subsystems | ||
45 | */ | ||
46 | struct mutex mutex; | ||
47 | |||
48 | struct nvkm_mm _mm; | ||
49 | /* | ||
50 | * Just points to _mm. We need this to avoid embedding | ||
51 | * struct nvkm_mm in os.h | ||
52 | */ | ||
53 | struct nvkm_mm *mm; | ||
54 | struct iommu_domain *domain; | ||
55 | unsigned long pgshift; | ||
56 | } iommu; | ||
39 | }; | 57 | }; |
40 | 58 | ||
41 | struct nouveau_platform_device { | 59 | struct nouveau_platform_device { |