Commit 58fd9375 authored by Alexandre Courbot's avatar Alexandre Courbot Committed by Ben Skeggs

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: default avatarAlexandre Courbot <acourbot@nvidia.com>
Signed-off-by: default avatarBen Skeggs <bskeggs@redhat.com>
parent 5dc240bc
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include <linux/of.h> #include <linux/of.h>
#include <linux/reset.h> #include <linux/reset.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/iommu.h>
#include <soc/tegra/fuse.h> #include <soc/tegra/fuse.h>
#include <soc/tegra/pmc.h> #include <soc/tegra/pmc.h>
...@@ -91,6 +92,71 @@ static int nouveau_platform_power_down(struct nouveau_platform_gpu *gpu) ...@@ -91,6 +92,71 @@ static int nouveau_platform_power_down(struct nouveau_platform_gpu *gpu)
return 0; 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) static int nouveau_platform_probe(struct platform_device *pdev)
{ {
struct nouveau_platform_gpu *gpu; struct nouveau_platform_gpu *gpu;
...@@ -118,6 +184,8 @@ static int nouveau_platform_probe(struct platform_device *pdev) ...@@ -118,6 +184,8 @@ static int nouveau_platform_probe(struct platform_device *pdev)
if (IS_ERR(gpu->clk_pwr)) if (IS_ERR(gpu->clk_pwr))
return PTR_ERR(gpu->clk_pwr); return PTR_ERR(gpu->clk_pwr);
nouveau_platform_probe_iommu(&pdev->dev, gpu);
err = nouveau_platform_power_up(gpu); err = nouveau_platform_power_up(gpu);
if (err) if (err)
return err; return err;
...@@ -154,10 +222,15 @@ static int nouveau_platform_remove(struct platform_device *pdev) ...@@ -154,10 +222,15 @@ static int nouveau_platform_remove(struct platform_device *pdev)
struct nouveau_drm *drm = nouveau_drm(drm_dev); struct nouveau_drm *drm = nouveau_drm(drm_dev);
struct nvkm_device *device = nvxx_device(&drm->device); struct nvkm_device *device = nvxx_device(&drm->device);
struct nouveau_platform_gpu *gpu = nv_device_to_platform(device)->gpu; struct nouveau_platform_gpu *gpu = nv_device_to_platform(device)->gpu;
int err;
nouveau_drm_device_remove(drm_dev); 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) #if IS_ENABLED(CONFIG_OF)
......
...@@ -24,10 +24,12 @@ ...@@ -24,10 +24,12 @@
#define __NOUVEAU_PLATFORM_H__ #define __NOUVEAU_PLATFORM_H__
#include "core/device.h" #include "core/device.h"
#include "core/mm.h"
struct reset_control; struct reset_control;
struct clk; struct clk;
struct regulator; struct regulator;
struct iommu_domain;
struct platform_driver; struct platform_driver;
struct nouveau_platform_gpu { struct nouveau_platform_gpu {
...@@ -36,6 +38,22 @@ struct nouveau_platform_gpu { ...@@ -36,6 +38,22 @@ struct nouveau_platform_gpu {
struct clk *clk_pwr; struct clk *clk_pwr;
struct regulator *vdd; 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 { struct nouveau_platform_device {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment