aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexandre Courbot <acourbot@nvidia.com>2015-02-20 04:23:03 -0500
committerBen Skeggs <bskeggs@redhat.com>2015-04-14 03:00:44 -0400
commit58fd9375c2c5344e8ab6ef9971635bc59cd39658 (patch)
tree85727259dfb9e87703762bcfc5f110c96c4b554c
parent5dc240bcfe9a8d30b151be58b174261ba388cb01 (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.c75
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_platform.h18
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
95static 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
139detach_device:
140 iommu_detach_device(gpu->iommu.domain, dev);
141
142free_domain:
143 iommu_domain_free(gpu->iommu.domain);
144
145error:
146 gpu->iommu.domain = NULL;
147 gpu->iommu.pgshift = 0;
148 dev_err(dev, "cannot initialize IOMMU MM\n");
149}
150
151static 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
94static int nouveau_platform_probe(struct platform_device *pdev) 160static 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
28struct reset_control; 29struct reset_control;
29struct clk; 30struct clk;
30struct regulator; 31struct regulator;
32struct iommu_domain;
31struct platform_driver; 33struct platform_driver;
32 34
33struct nouveau_platform_gpu { 35struct 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
41struct nouveau_platform_device { 59struct nouveau_platform_device {