From: Alex Deucher <alexander.deuc...@amd.com>

The doorbell aperture is a PCI BAR whose pages can be
mapped to compute resources for things like wptrs
for userspace queues.

This patch maps the BAR and sets up a simple allocator
to allocate pages from the BAR.

Signed-off-by: Alex Deucher <alexander.deucher at amd.com>
---
 drivers/gpu/drm/radeon/cik.c           |   38 +++++++++++++
 drivers/gpu/drm/radeon/radeon.h        |   21 +++++++
 drivers/gpu/drm/radeon/radeon_device.c |   94 ++++++++++++++++++++++++++++++++
 3 files changed, 153 insertions(+), 0 deletions(-)

diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c
index bb7dbc4..5c28fa5 100644
--- a/drivers/gpu/drm/radeon/cik.c
+++ b/drivers/gpu/drm/radeon/cik.c
@@ -121,6 +121,44 @@ u32 cik_get_xclk(struct radeon_device *rdev)
        return reference_clock;
 }

+/**
+ * cik_mm_rdoorbell - read a doorbell dword
+ *
+ * @rdev: radeon_device pointer
+ * @offset: byte offset into the aperture
+ *
+ * Returns the value in the doorbell aperture at the
+ * requested offset (CIK).
+ */
+u32 cik_mm_rdoorbell(struct radeon_device *rdev, u32 offset)
+{
+       if (offset < rdev->doorbell.size) {
+               return readl(((void __iomem *)rdev->doorbell.ptr) + offset);
+       } else {
+               DRM_ERROR("reading beyond doorbell aperture: 0x%08x!\n", 
offset);
+               return 0;
+       }
+}
+
+/**
+ * cik_mm_wdoorbell - write a doorbell dword
+ *
+ * @rdev: radeon_device pointer
+ * @offset: byte offset into the aperture
+ * @v: value to write
+ *
+ * Writes @v to the doorbell aperture at the
+ * requested offset (CIK).
+ */
+void cik_mm_wdoorbell(struct radeon_device *rdev, u32 offset, u32 v)
+{
+       if (offset < rdev->doorbell.size) {
+               writel(v, ((void __iomem *)rdev->doorbell.ptr) + offset);
+       } else {
+               DRM_ERROR("writing beyond doorbell aperture: 0x%08x!\n", 
offset);
+       }
+}
+
 #define BONAIRE_IO_MC_REGS_SIZE 36

 static const u32 bonaire_io_mc_regs[BONAIRE_IO_MC_REGS_SIZE][2] =
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h
index ad4e68a..a2a3430 100644
--- a/drivers/gpu/drm/radeon/radeon.h
+++ b/drivers/gpu/drm/radeon/radeon.h
@@ -556,6 +556,20 @@ struct radeon_scratch {
 int radeon_scratch_get(struct radeon_device *rdev, uint32_t *reg);
 void radeon_scratch_free(struct radeon_device *rdev, uint32_t reg);

+/*
+ * GPU doorbell structures, functions & helpers
+ */
+struct radeon_doorbell {
+       u32                     num_pages;
+       bool                    free[1024];
+       /* doorbell mmio */
+       resource_size_t                 base;
+       resource_size_t                 size;
+       void __iomem                    *ptr;
+};
+
+int radeon_doorbell_get(struct radeon_device *rdev, u32 *page);
+void radeon_doorbell_free(struct radeon_device *rdev, u32 doorbell);

 /*
  * IRQS.
@@ -1711,6 +1725,7 @@ struct radeon_device {
        struct radeon_gart              gart;
        struct radeon_mode_info         mode_info;
        struct radeon_scratch           scratch;
+       struct radeon_doorbell          doorbell;
        struct radeon_mman              mman;
        struct radeon_fence_driver      fence_drv[RADEON_NUM_RINGS];
        wait_queue_head_t               fence_queue;
@@ -1784,6 +1799,9 @@ void r100_mm_wreg(struct radeon_device *rdev, uint32_t 
reg, uint32_t v,
 u32 r100_io_rreg(struct radeon_device *rdev, u32 reg);
 void r100_io_wreg(struct radeon_device *rdev, u32 reg, u32 v);

+u32 cik_mm_rdoorbell(struct radeon_device *rdev, u32 offset);
+void cik_mm_wdoorbell(struct radeon_device *rdev, u32 offset, u32 v);
+
 /*
  * Cast helper
  */
@@ -1833,6 +1851,9 @@ void r100_io_wreg(struct radeon_device *rdev, u32 reg, 
u32 v);
 #define RREG32_IO(reg) r100_io_rreg(rdev, (reg))
 #define WREG32_IO(reg, v) r100_io_wreg(rdev, (reg), (v))

+#define RDOORBELL32(offset) cik_mm_rdoorbell(rdev, (offset))
+#define WDOORBELL32(offset, v) cik_mm_wdoorbell(rdev, (offset), (v))
+
 /*
  * Indirect registers accessor
  */
diff --git a/drivers/gpu/drm/radeon/radeon_device.c 
b/drivers/gpu/drm/radeon/radeon_device.c
index 4e97ff7..82335e3 100644
--- a/drivers/gpu/drm/radeon/radeon_device.c
+++ b/drivers/gpu/drm/radeon/radeon_device.c
@@ -232,6 +232,94 @@ void radeon_scratch_free(struct radeon_device *rdev, 
uint32_t reg)
 }

 /*
+ * GPU doorbell aperture helpers function.
+ */
+/**
+ * radeon_doorbell_init - Init doorbell driver information.
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Init doorbell driver information (CIK)
+ * Returns 0 on success, error on failure.
+ */
+int radeon_doorbell_init(struct radeon_device *rdev)
+{
+       int i;
+
+       /* doorbell bar mapping */
+       rdev->doorbell.base = pci_resource_start(rdev->pdev, 2);
+       rdev->doorbell.size = pci_resource_len(rdev->pdev, 2);
+
+       /* limit to 4 MB for now */
+       if (rdev->doorbell.size > (4 * 1024 * 1024))
+               rdev->doorbell.size = 4 * 1024 * 1024;
+
+       rdev->doorbell.ptr = ioremap(rdev->doorbell.base, rdev->doorbell.size);
+       if (rdev->doorbell.ptr == NULL) {
+               return -ENOMEM;
+       }
+       DRM_INFO("doorbell mmio base: 0x%08X\n", (uint32_t)rdev->doorbell.base);
+       DRM_INFO("doorbell mmio size: %u\n", (unsigned)rdev->doorbell.size);
+
+       rdev->doorbell.num_pages = rdev->doorbell.size / PAGE_SIZE;
+
+       for (i = 0; i < rdev->doorbell.num_pages; i++) {
+               rdev->doorbell.free[i] = true;
+       }
+       return 0;
+}
+
+/**
+ * radeon_doorbell_fini - Tear down doorbell driver information.
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Tear down doorbell driver information (CIK)
+ */
+void radeon_doorbell_fini(struct radeon_device *rdev)
+{
+       iounmap(rdev->doorbell.ptr);
+       rdev->doorbell.ptr = NULL;
+}
+
+/**
+ * radeon_doorbell_get - Allocate a doorbell page
+ *
+ * @rdev: radeon_device pointer
+ * @doorbell: doorbell page number
+ *
+ * Allocate a doorbell page for use by the driver (all asics).
+ * Returns 0 on success or -EINVAL on failure.
+ */
+int radeon_doorbell_get(struct radeon_device *rdev, u32 *doorbell)
+{
+       int i;
+
+       for (i = 0; i < rdev->doorbell.num_pages; i++) {
+               if (rdev->doorbell.free[i]) {
+                       rdev->doorbell.free[i] = false;
+                       *doorbell = i;
+                       return 0;
+               }
+       }
+       return -EINVAL;
+}
+
+/**
+ * radeon_doorbell_free - Free a doorbell page
+ *
+ * @rdev: radeon_device pointer
+ * @doorbell: doorbell page number
+ *
+ * Free a doorbell page allocated for use by the driver (all asics)
+ */
+void radeon_doorbell_free(struct radeon_device *rdev, u32 doorbell)
+{
+       if (doorbell < rdev->doorbell.num_pages)
+               rdev->doorbell.free[doorbell] = true;
+}
+
+/*
  * radeon_wb_*()
  * Writeback is the the method by which the the GPU updates special pages
  * in memory with the status of certain GPU events (fences, ring pointers,
@@ -1162,6 +1250,10 @@ int radeon_device_init(struct radeon_device *rdev,
        DRM_INFO("register mmio base: 0x%08X\n", (uint32_t)rdev->rmmio_base);
        DRM_INFO("register mmio size: %u\n", (unsigned)rdev->rmmio_size);

+       /* doorbell bar mapping */
+       if (rdev->family >= CHIP_BONAIRE)
+               radeon_doorbell_init(rdev);
+
        /* io port mapping */
        for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
                if (pci_resource_flags(rdev->pdev, i) & IORESOURCE_IO) {
@@ -1239,6 +1331,8 @@ void radeon_device_fini(struct radeon_device *rdev)
        rdev->rio_mem = NULL;
        iounmap(rdev->rmmio);
        rdev->rmmio = NULL;
+       if (rdev->family >= CHIP_BONAIRE)
+               radeon_doorbell_fini(rdev);
        radeon_debugfs_remove_files(rdev);
 }

-- 
1.7.7.5

Reply via email to