summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexandre Courbot <acourbot@nvidia.com>2015-02-20 18:23:03 +0900
committerBen Skeggs <bskeggs@redhat.com>2015-03-17 09:45:12 +1000
commit4a50f39203e1275327689b4da837b9df1c999909 (patch)
treeec817265309717acea00624db227a143f36be5a8
parent3bc5960645a5d152e74ee92582b0ce3ed0f8cf26 (diff)
downloadnouveau-4a50f39203e1275327689b4da837b9df1c999909.tar.gz
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--drm/nouveau/nouveau_platform.c75
-rw-r--r--drm/nouveau/nouveau_platform.h18
-rw-r--r--lib/include/nvif/os.h32
3 files changed, 124 insertions, 1 deletions
diff --git a/drm/nouveau/nouveau_platform.c b/drm/nouveau/nouveau_platform.c
index dc5900bf5..369198245 100644
--- a/drm/nouveau/nouveau_platform.c
+++ b/drm/nouveau/nouveau_platform.c
@@ -27,6 +27,7 @@
#include <linux/of.h>
#include <linux/reset.h>
#include <linux/regulator/consumer.h>
+#include <linux/iommu.h>
#include <soc/tegra/fuse.h>
#include <soc/tegra/pmc.h>
@@ -91,6 +92,71 @@ static int nouveau_platform_power_down(struct nouveau_platform_gpu *gpu)
return 0;
}
+static void nouveau_platform_probe_iommu(struct device *dev,
+ struct nouveau_platform_gpu *gpu)
+{
+ int err;
+ unsigned long pgsize_bitmap;
+
+ mutex_init(&gpu->iommu.mutex);
+
+ if (iommu_present(&platform_bus_type)) {
+ gpu->iommu.domain = iommu_domain_alloc(&platform_bus_type);
+ if (IS_ERR(gpu->iommu.domain))
+ goto error;
+
+ /*
+ * A IOMMU is only usable if it supports page sizes smaller
+ * or equal to the system's PAGE_SIZE, with a preference if
+ * both are equal.
+ */
+ pgsize_bitmap = gpu->iommu.domain->ops->pgsize_bitmap;
+ if (pgsize_bitmap & PAGE_SIZE) {
+ gpu->iommu.pgshift = PAGE_SHIFT;
+ } else {
+ gpu->iommu.pgshift = fls(pgsize_bitmap & ~PAGE_MASK);
+ if (gpu->iommu.pgshift == 0) {
+ dev_warn(dev, "unsupported IOMMU page size\n");
+ goto free_domain;
+ }
+ gpu->iommu.pgshift -= 1;
+ }
+
+ err = iommu_attach_device(gpu->iommu.domain, dev);
+ if (err)
+ goto free_domain;
+
+ err = nvkm_mm_init(&gpu->iommu._mm, 0,
+ (1ULL << 40) >> gpu->iommu.pgshift, 1);
+ if (err)
+ goto detach_device;
+
+ gpu->iommu.mm = &gpu->iommu._mm;
+ }
+
+ return;
+
+detach_device:
+ iommu_detach_device(gpu->iommu.domain, dev);
+
+free_domain:
+ iommu_domain_free(gpu->iommu.domain);
+
+error:
+ gpu->iommu.domain = NULL;
+ gpu->iommu.pgshift = 0;
+ dev_err(dev, "cannot initialize IOMMU MM\n");
+}
+
+static void nouveau_platform_remove_iommu(struct device *dev,
+ struct nouveau_platform_gpu *gpu)
+{
+ if (gpu->iommu.domain) {
+ iommu_detach_device(gpu->iommu.domain, dev);
+ iommu_domain_free(gpu->iommu.domain);
+ }
+}
+
static int nouveau_platform_probe(struct platform_device *pdev)
{
struct nouveau_platform_gpu *gpu;
@@ -118,6 +184,8 @@ static int nouveau_platform_probe(struct platform_device *pdev)
if (IS_ERR(gpu->clk_pwr))
return PTR_ERR(gpu->clk_pwr);
+ nouveau_platform_probe_iommu(&pdev->dev, gpu);
+
err = nouveau_platform_power_up(gpu);
if (err)
return err;
@@ -154,10 +222,15 @@ static int nouveau_platform_remove(struct platform_device *pdev)
struct nouveau_drm *drm = nouveau_drm(drm_dev);
struct nvkm_device *device = nvxx_device(&drm->device);
struct nouveau_platform_gpu *gpu = nv_device_to_platform(device)->gpu;
+ int err;
nouveau_drm_device_remove(drm_dev);
- return nouveau_platform_power_down(gpu);
+ err = nouveau_platform_power_down(gpu);
+
+ nouveau_platform_remove_iommu(&pdev->dev, gpu);
+
+ return err;
}
#if IS_ENABLED(CONFIG_OF)
diff --git a/drm/nouveau/nouveau_platform.h b/drm/nouveau/nouveau_platform.h
index 268bb7213..392874cf4 100644
--- a/drm/nouveau/nouveau_platform.h
+++ b/drm/nouveau/nouveau_platform.h
@@ -24,10 +24,12 @@
#define __NOUVEAU_PLATFORM_H__
#include "core/device.h"
+#include "core/mm.h"
struct reset_control;
struct clk;
struct regulator;
+struct iommu_domain;
struct platform_driver;
struct nouveau_platform_gpu {
@@ -36,6 +38,22 @@ struct nouveau_platform_gpu {
struct clk *clk_pwr;
struct regulator *vdd;
+
+ struct {
+ /*
+ * Protects accesses to mm from subsystems
+ */
+ struct mutex mutex;
+
+ struct nvkm_mm _mm;
+ /*
+ * Just points to _mm. We need this to avoid embedding
+ * struct nvkm_mm in os.h
+ */
+ struct nvkm_mm *mm;
+ struct iommu_domain *domain;
+ unsigned long pgshift;
+ } iommu;
};
struct nouveau_platform_device {
diff --git a/lib/include/nvif/os.h b/lib/include/nvif/os.h
index b4d307e3a..275fa84ad 100644
--- a/lib/include/nvif/os.h
+++ b/lib/include/nvif/os.h
@@ -715,6 +715,29 @@ dma_free_attrs(struct device *dev, size_t sz, void *vaddr, dma_addr_t bus,
/******************************************************************************
+ * IOMMU
+ *****************************************************************************/
+struct iommu_domain;
+
+#define IOMMU_READ (1 << 0)
+#define IOMMU_WRITE (1 << 1)
+#define IOMMU_CACHE (1 << 2) /* DMA cache coherency */
+#define IOMMU_NOEXEC (1 << 3)
+
+static inline int
+iommu_map(struct iommu_domain *domain, unsigned long iova, dma_addr_t paddr,
+ size_t size, int prot)
+{
+ return 0;
+}
+
+static inline size_t
+iommu_unmap(struct iommu_domain *domain, unsigned long iova, size_t size)
+{
+ return 0;
+}
+
+/******************************************************************************
* PCI
*****************************************************************************/
#include <pciaccess.h>
@@ -1208,9 +1231,18 @@ regulator_get_voltage(struct regulator *regulator)
* nouveau drm platform device
*****************************************************************************/
+struct nvkm_mm;
+
struct nouveau_platform_gpu {
struct clk *clk;
struct regulator *vdd;
+
+ struct {
+ struct mutex mutex;
+ struct nvkm_mm *mm;
+ struct iommu_domain *domain;
+ unsigned long pgshift;
+ } iommu;
};
struct nouveau_platform_device {