drm/radeon: implement simple doorbell page allocator
authorAlex Deucher <alexander.deucher@amd.com>
Mon, 4 Mar 2013 17:47:46 +0000 (12:47 -0500)
committerAlex Deucher <alexander.deucher@amd.com>
Thu, 27 Jun 2013 14:49:07 +0000 (10:49 -0400)
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@amd.com>
drivers/gpu/drm/radeon/cik.c
drivers/gpu/drm/radeon/radeon.h
drivers/gpu/drm/radeon/radeon_device.c

index a61c373..a8161d0 100644 (file)
@@ -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] =
index 064a579..a74d985 100644 (file)
@@ -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.
@@ -1710,6 +1724,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;
@@ -1783,6 +1798,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
  */
@@ -1832,6 +1850,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
  */
index 4e97ff7..82335e3 100644 (file)
@@ -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);
 }