From 740f09bffde20d9207497bd107d50941ca21278a Mon Sep 17 00:00:00 2001
From: Robert Noland <rnoland@2hip.net>
Date: Sat, 6 Sep 2008 21:08:33 -0400
Subject: [FreeBSD] IGP gart needs to be un-cached.

Airlied inadvertently discovered that the IGP gart needs to be un-cached
for radeon rs485 and rs690 to work.  Initial tests by placing a wbinvd()
after allocating the gart were successful.  This is an attempt at a more
appropriate method of achieving success.
---
 bsd-core/ati_pcigart.c | 91 ++++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 73 insertions(+), 18 deletions(-)

diff --git a/bsd-core/ati_pcigart.c b/bsd-core/ati_pcigart.c
index c4453ed5..5feeeba8 100644
--- a/bsd-core/ati_pcigart.c
+++ b/bsd-core/ati_pcigart.c
@@ -39,31 +39,86 @@
 #define ATI_PCIE_WRITE 0x4
 #define ATI_PCIE_READ 0x8
 
-static int drm_ati_alloc_pcigart_table(struct drm_device *dev,
-				       struct drm_ati_pcigart_info *gart_info)
+static void
+drm_ati_alloc_pcigart_table_cb(void *arg, bus_dma_segment_t *segs,
+			       int nsegs, int error)
 {
-	drm_dma_handle_t *dmah;
+	struct drm_dma_handle *dmah = arg;
+
+	if (error != 0)
+		return;
+
+	KASSERT(nsegs == 1,
+	    ("drm_ati_alloc_pcigart_table_cb: bad dma segment count"));
+
+	dmah->busaddr = segs[0].ds_addr;
+}
+
+static int
+drm_ati_alloc_pcigart_table(struct drm_device *dev,
+			    struct drm_ati_pcigart_info *gart_info)
+{
+	struct drm_dma_handle *dmah;
+	int flags, ret;
+
+	dmah = malloc(sizeof(struct drm_dma_handle), M_DRM, M_ZERO | M_NOWAIT);
+	if (dmah == NULL)
+		return ENOMEM;
 
 	DRM_UNLOCK();
-	dmah = drm_pci_alloc(dev, gart_info->table_size, PAGE_SIZE,
-	    gart_info->table_mask);
+	ret = bus_dma_tag_create(NULL, PAGE_SIZE, 0, /* tag, align, boundary */
+	    gart_info->table_mask, BUS_SPACE_MAXADDR, /* lowaddr, highaddr */
+	    NULL, NULL, /* filtfunc, filtfuncargs */
+	    gart_info->table_size, 1, /* maxsize, nsegs */
+	    gart_info->table_size, /* maxsegsize */
+	    BUS_DMA_ALLOCNOW, NULL, NULL, /* flags, lockfunc, lockfuncargs */
+	    &dmah->tag);
+	if (ret != 0) {
+		free(dmah, M_DRM);
+		return ENOMEM;
+	}
+
+	flags = BUS_DMA_NOWAIT | BUS_DMA_ZERO;
+	if (gart_info->gart_reg_if == DRM_ATI_GART_IGP)
+	    flags |= BUS_DMA_NOCACHE;
+	
+	ret = bus_dmamem_alloc(dmah->tag, &dmah->vaddr, flags, &dmah->map);
+	if (ret != 0) {
+		bus_dma_tag_destroy(dmah->tag);
+		free(dmah, M_DRM);
+		return ENOMEM;
+	}
 	DRM_LOCK();
-	if (dmah == NULL)
+
+	ret = bus_dmamap_load(dmah->tag, dmah->map, dmah->vaddr,
+	    gart_info->table_size, drm_ati_alloc_pcigart_table_cb, dmah, 0);
+	if (ret != 0) {
+		bus_dmamem_free(dmah->tag, dmah->vaddr, dmah->map);
+		bus_dma_tag_destroy(dmah->tag);
+		free(dmah, M_DRM);
 		return ENOMEM;
+	}
 
 	dev->sg->dmah = dmah;
 
 	return 0;
 }
 
-static void drm_ati_free_pcigart_table(struct drm_device *dev,
-				       struct drm_ati_pcigart_info *gart_info)
+static void
+drm_ati_free_pcigart_table(struct drm_device *dev,
+			   struct drm_ati_pcigart_info *gart_info)
 {
-	drm_pci_free(dev, dev->sg->dmah);
+	struct drm_dma_handle *dmah = dev->sg->dmah;
+
+	bus_dmamem_free(dmah->tag, dmah->vaddr, dmah->map);
+	bus_dma_tag_destroy(dmah->tag);
+	free(dmah, M_DRM);
 	dev->sg->dmah = NULL;
 }
 
-int drm_ati_pcigart_cleanup(struct drm_device *dev, struct drm_ati_pcigart_info *gart_info)
+int
+drm_ati_pcigart_cleanup(struct drm_device *dev,
+			struct drm_ati_pcigart_info *gart_info)
 {
 	/* we need to support large memory configurations */
 	if (dev->sg == NULL) {
@@ -82,17 +137,17 @@ int drm_ati_pcigart_cleanup(struct drm_device *dev, struct drm_ati_pcigart_info
 	return 1;
 }
 
-int drm_ati_pcigart_init(struct drm_device *dev,
-			 struct drm_ati_pcigart_info *gart_info)
+int
+drm_ati_pcigart_init(struct drm_device *dev,
+		     struct drm_ati_pcigart_info *gart_info)
 {
-
 	void *address = NULL;
 	unsigned long pages;
 	u32 *pci_gart, page_base;
 	dma_addr_t bus_address = 0;
+	dma_addr_t entry_addr;
 	int i, j, ret = 0;
 	int max_pages;
-	dma_addr_t entry_addr;
 
 	/* we need to support large memory configurations */
 	if (dev->sg == NULL) {
@@ -134,12 +189,14 @@ int drm_ati_pcigart_init(struct drm_device *dev,
 			page_base = (u32) entry_addr & ATI_PCIGART_PAGE_MASK;
 			switch(gart_info->gart_reg_if) {
 			case DRM_ATI_GART_IGP:
-				page_base |= (upper_32_bits(entry_addr) & 0xff) << 4;
+				page_base |=
+				    (upper_32_bits(entry_addr) & 0xff) << 4;
 				page_base |= 0xc;
 				break;
 			case DRM_ATI_GART_PCIE:
 				page_base >>= 8;
-				page_base |= (upper_32_bits(entry_addr) & 0xff) << 24;
+				page_base |=
+				    (upper_32_bits(entry_addr) & 0xff) << 24;
 				page_base |= ATI_PCIE_READ | ATI_PCIE_WRITE;
 				break;
 			default:
@@ -152,8 +209,6 @@ int drm_ati_pcigart_init(struct drm_device *dev,
 		}
 	}
 
-	DRM_MEMORYBARRIER();
-
 	ret = 1;
 
     done:
-- 
cgit v1.2.3