diff mbox

[051/165] drm/radeon: implement simple doorbell page allocator

Message ID 1372253045-17042-52-git-send-email-alexdeucher@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Alex Deucher June 26, 2013, 1:22 p.m. UTC
From: Alex Deucher <alexander.deucher@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@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(-)

Comments

Jerome Glisse June 26, 2013, 12:57 p.m. UTC | #1
On Wed, Jun 26, 2013 at 09:22:11AM -0400, alexdeucher@gmail.com wrote:
> From: Alex Deucher <alexander.deucher@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.

This doorbell stuff is cryptic, is that some memory on the GPU ? Or is it
more like a register file ? ie what is backing the pci bar.

Also probably want to use bitmap as i dont think gcc will turn bool array
into a bitmap.

Cheers,
Jerome

> 
> Signed-off-by: Alex Deucher <alexander.deucher@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
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel
Alex Deucher June 26, 2013, 6:38 p.m. UTC | #2
On Wed, Jun 26, 2013 at 8:57 AM, Jerome Glisse <j.glisse@gmail.com> wrote:
> On Wed, Jun 26, 2013 at 09:22:11AM -0400, alexdeucher@gmail.com wrote:
>> From: Alex Deucher <alexander.deucher@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.
>
> This doorbell stuff is cryptic, is that some memory on the GPU ? Or is it
> more like a register file ? ie what is backing the pci bar.
>

There's no memory backing it.  It's just an aperture that the GPU can
selectively listens to.  You assign dw offsets from the doorbell
aperture for things like the wrptr for compute rings.  When the driver
or process write a new wtpr value to the offset assigned to it's
specific ring, the GPU starts processing it.  Like ringing a doorbell.

Alex

> Also probably want to use bitmap as i dont think gcc will turn bool array
> into a bitmap.
>
> Cheers,
> Jerome
>
>>
>> Signed-off-by: Alex Deucher <alexander.deucher@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
>>
>> _______________________________________________
>> dri-devel mailing list
>> dri-devel@lists.freedesktop.org
>> http://lists.freedesktop.org/mailman/listinfo/dri-devel
diff mbox

Patch

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);
 }