1*677dec6eSriastradh /*	$NetBSD: ati_pcigart.c,v 1.2 2021/12/18 23:45:42 riastradh Exp $	*/
21571a7a1Sriastradh 
31571a7a1Sriastradh /**
41571a7a1Sriastradh  * \file ati_pcigart.c
51571a7a1Sriastradh  * ATI PCI GART support
61571a7a1Sriastradh  *
71571a7a1Sriastradh  * \author Gareth Hughes <gareth@valinux.com>
81571a7a1Sriastradh  */
91571a7a1Sriastradh 
101571a7a1Sriastradh /*
111571a7a1Sriastradh  * Created: Wed Dec 13 21:52:19 2000 by gareth@valinux.com
121571a7a1Sriastradh  *
131571a7a1Sriastradh  * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
141571a7a1Sriastradh  * All Rights Reserved.
151571a7a1Sriastradh  *
161571a7a1Sriastradh  * Permission is hereby granted, free of charge, to any person obtaining a
171571a7a1Sriastradh  * copy of this software and associated documentation files (the "Software"),
181571a7a1Sriastradh  * to deal in the Software without restriction, including without limitation
191571a7a1Sriastradh  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
201571a7a1Sriastradh  * and/or sell copies of the Software, and to permit persons to whom the
211571a7a1Sriastradh  * Software is furnished to do so, subject to the following conditions:
221571a7a1Sriastradh  *
231571a7a1Sriastradh  * The above copyright notice and this permission notice (including the next
241571a7a1Sriastradh  * paragraph) shall be included in all copies or substantial portions of the
251571a7a1Sriastradh  * Software.
261571a7a1Sriastradh  *
271571a7a1Sriastradh  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
281571a7a1Sriastradh  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
291571a7a1Sriastradh  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
301571a7a1Sriastradh  * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
311571a7a1Sriastradh  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
321571a7a1Sriastradh  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
331571a7a1Sriastradh  * DEALINGS IN THE SOFTWARE.
341571a7a1Sriastradh  */
351571a7a1Sriastradh 
361571a7a1Sriastradh #include <sys/cdefs.h>
37*677dec6eSriastradh __KERNEL_RCSID(0, "$NetBSD: ati_pcigart.c,v 1.2 2021/12/18 23:45:42 riastradh Exp $");
381571a7a1Sriastradh 
391571a7a1Sriastradh #include <linux/export.h>
401571a7a1Sriastradh 
411571a7a1Sriastradh #include <drm/drm_device.h>
421571a7a1Sriastradh #include <drm/drm_pci.h>
431571a7a1Sriastradh #include <drm/drm_print.h>
441571a7a1Sriastradh 
451571a7a1Sriastradh #include "ati_pcigart.h"
461571a7a1Sriastradh 
471571a7a1Sriastradh # define ATI_PCIGART_PAGE_SIZE		4096	/**< PCI GART page size */
481571a7a1Sriastradh 
drm_ati_alloc_pcigart_table(struct drm_device * dev,struct drm_ati_pcigart_info * gart_info)491571a7a1Sriastradh static int drm_ati_alloc_pcigart_table(struct drm_device *dev,
501571a7a1Sriastradh 				       struct drm_ati_pcigart_info *gart_info)
511571a7a1Sriastradh {
521571a7a1Sriastradh 	gart_info->table_handle = drm_pci_alloc(dev, gart_info->table_size,
531571a7a1Sriastradh 						PAGE_SIZE);
541571a7a1Sriastradh 	if (gart_info->table_handle == NULL)
551571a7a1Sriastradh 		return -ENOMEM;
561571a7a1Sriastradh 
571571a7a1Sriastradh 	return 0;
581571a7a1Sriastradh }
591571a7a1Sriastradh 
drm_ati_free_pcigart_table(struct drm_device * dev,struct drm_ati_pcigart_info * gart_info)601571a7a1Sriastradh static void drm_ati_free_pcigart_table(struct drm_device *dev,
611571a7a1Sriastradh 				       struct drm_ati_pcigart_info *gart_info)
621571a7a1Sriastradh {
631571a7a1Sriastradh 	drm_pci_free(dev, gart_info->table_handle);
641571a7a1Sriastradh 	gart_info->table_handle = NULL;
651571a7a1Sriastradh }
661571a7a1Sriastradh 
drm_ati_pcigart_cleanup(struct drm_device * dev,struct drm_ati_pcigart_info * gart_info)671571a7a1Sriastradh int drm_ati_pcigart_cleanup(struct drm_device *dev, struct drm_ati_pcigart_info *gart_info)
681571a7a1Sriastradh {
691571a7a1Sriastradh 	struct drm_sg_mem *entry = dev->sg;
701571a7a1Sriastradh 	unsigned long pages;
711571a7a1Sriastradh 	int i;
721571a7a1Sriastradh 	int max_pages;
731571a7a1Sriastradh 
741571a7a1Sriastradh 	/* we need to support large memory configurations */
751571a7a1Sriastradh 	if (!entry) {
761571a7a1Sriastradh 		DRM_ERROR("no scatter/gather memory!\n");
771571a7a1Sriastradh 		return 0;
781571a7a1Sriastradh 	}
791571a7a1Sriastradh 
801571a7a1Sriastradh 	if (gart_info->bus_addr) {
811571a7a1Sriastradh 
821571a7a1Sriastradh 		max_pages = (gart_info->table_size / sizeof(u32));
831571a7a1Sriastradh 		pages = (entry->pages <= max_pages)
841571a7a1Sriastradh 		  ? entry->pages : max_pages;
851571a7a1Sriastradh 
861571a7a1Sriastradh 		for (i = 0; i < pages; i++) {
871571a7a1Sriastradh 			if (!entry->busaddr[i])
881571a7a1Sriastradh 				break;
891571a7a1Sriastradh 			pci_unmap_page(dev->pdev, entry->busaddr[i],
901571a7a1Sriastradh 					 PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
911571a7a1Sriastradh 		}
921571a7a1Sriastradh 
931571a7a1Sriastradh 		if (gart_info->gart_table_location == DRM_ATI_GART_MAIN)
941571a7a1Sriastradh 			gart_info->bus_addr = 0;
951571a7a1Sriastradh 	}
961571a7a1Sriastradh 
971571a7a1Sriastradh 	if (gart_info->gart_table_location == DRM_ATI_GART_MAIN &&
981571a7a1Sriastradh 	    gart_info->table_handle) {
991571a7a1Sriastradh 		drm_ati_free_pcigart_table(dev, gart_info);
1001571a7a1Sriastradh 	}
1011571a7a1Sriastradh 
1021571a7a1Sriastradh 	return 1;
1031571a7a1Sriastradh }
1041571a7a1Sriastradh 
drm_ati_pcigart_init(struct drm_device * dev,struct drm_ati_pcigart_info * gart_info)1051571a7a1Sriastradh int drm_ati_pcigart_init(struct drm_device *dev, struct drm_ati_pcigart_info *gart_info)
1061571a7a1Sriastradh {
1071571a7a1Sriastradh 	struct drm_local_map *map = &gart_info->mapping;
1081571a7a1Sriastradh 	struct drm_sg_mem *entry = dev->sg;
1091571a7a1Sriastradh 	void *address = NULL;
1101571a7a1Sriastradh 	unsigned long pages;
1111571a7a1Sriastradh 	u32 *pci_gart = NULL, page_base, gart_idx;
1121571a7a1Sriastradh 	dma_addr_t bus_address = 0;
1131571a7a1Sriastradh 	int i, j, ret = -ENOMEM;
1141571a7a1Sriastradh 	int max_ati_pages, max_real_pages;
1151571a7a1Sriastradh 
1161571a7a1Sriastradh 	if (!entry) {
1171571a7a1Sriastradh 		DRM_ERROR("no scatter/gather memory!\n");
1181571a7a1Sriastradh 		goto done;
1191571a7a1Sriastradh 	}
1201571a7a1Sriastradh 
1211571a7a1Sriastradh 	if (gart_info->gart_table_location == DRM_ATI_GART_MAIN) {
1221571a7a1Sriastradh 		DRM_DEBUG("PCI: no table in VRAM: using normal RAM\n");
1231571a7a1Sriastradh 
1241571a7a1Sriastradh 		if (pci_set_dma_mask(dev->pdev, gart_info->table_mask)) {
1251571a7a1Sriastradh 			DRM_ERROR("fail to set dma mask to 0x%Lx\n",
1261571a7a1Sriastradh 				  (unsigned long long)gart_info->table_mask);
1271571a7a1Sriastradh 			ret = -EFAULT;
1281571a7a1Sriastradh 			goto done;
1291571a7a1Sriastradh 		}
1301571a7a1Sriastradh 
1311571a7a1Sriastradh 		ret = drm_ati_alloc_pcigart_table(dev, gart_info);
1321571a7a1Sriastradh 		if (ret) {
1331571a7a1Sriastradh 			DRM_ERROR("cannot allocate PCI GART page!\n");
1341571a7a1Sriastradh 			goto done;
1351571a7a1Sriastradh 		}
1361571a7a1Sriastradh 
1371571a7a1Sriastradh 		pci_gart = gart_info->table_handle->vaddr;
1381571a7a1Sriastradh 		address = gart_info->table_handle->vaddr;
1391571a7a1Sriastradh 		bus_address = gart_info->table_handle->busaddr;
1401571a7a1Sriastradh 	} else {
1411571a7a1Sriastradh 		address = gart_info->addr;
1421571a7a1Sriastradh 		bus_address = gart_info->bus_addr;
1431571a7a1Sriastradh 		DRM_DEBUG("PCI: Gart Table: VRAM %08LX mapped at %08lX\n",
1441571a7a1Sriastradh 			  (unsigned long long)bus_address,
1451571a7a1Sriastradh 			  (unsigned long)address);
1461571a7a1Sriastradh 	}
1471571a7a1Sriastradh 
1481571a7a1Sriastradh 
1491571a7a1Sriastradh 	max_ati_pages = (gart_info->table_size / sizeof(u32));
1501571a7a1Sriastradh 	max_real_pages = max_ati_pages / (PAGE_SIZE / ATI_PCIGART_PAGE_SIZE);
1511571a7a1Sriastradh 	pages = (entry->pages <= max_real_pages)
1521571a7a1Sriastradh 	    ? entry->pages : max_real_pages;
1531571a7a1Sriastradh 
1541571a7a1Sriastradh 	if (gart_info->gart_table_location == DRM_ATI_GART_MAIN) {
1551571a7a1Sriastradh 		memset(pci_gart, 0, max_ati_pages * sizeof(u32));
1561571a7a1Sriastradh 	} else {
1571571a7a1Sriastradh 		memset_io((void __iomem *)map->handle, 0, max_ati_pages * sizeof(u32));
1581571a7a1Sriastradh 	}
1591571a7a1Sriastradh 
1601571a7a1Sriastradh 	gart_idx = 0;
1611571a7a1Sriastradh 	for (i = 0; i < pages; i++) {
1621571a7a1Sriastradh 		/* we need to support large memory configurations */
1631571a7a1Sriastradh 		entry->busaddr[i] = pci_map_page(dev->pdev, entry->pagelist[i],
1641571a7a1Sriastradh 						 0, PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
1651571a7a1Sriastradh 		if (pci_dma_mapping_error(dev->pdev, entry->busaddr[i])) {
1661571a7a1Sriastradh 			DRM_ERROR("unable to map PCIGART pages!\n");
1671571a7a1Sriastradh 			drm_ati_pcigart_cleanup(dev, gart_info);
1681571a7a1Sriastradh 			address = NULL;
1691571a7a1Sriastradh 			bus_address = 0;
1701571a7a1Sriastradh 			ret = -ENOMEM;
1711571a7a1Sriastradh 			goto done;
1721571a7a1Sriastradh 		}
1731571a7a1Sriastradh 		page_base = (u32) entry->busaddr[i];
1741571a7a1Sriastradh 
1751571a7a1Sriastradh 		for (j = 0; j < (PAGE_SIZE / ATI_PCIGART_PAGE_SIZE); j++) {
1761571a7a1Sriastradh 			u32 offset;
1771571a7a1Sriastradh 			u32 val;
1781571a7a1Sriastradh 
1791571a7a1Sriastradh 			switch(gart_info->gart_reg_if) {
1801571a7a1Sriastradh 			case DRM_ATI_GART_IGP:
1811571a7a1Sriastradh 				val = page_base | 0xc;
1821571a7a1Sriastradh 				break;
1831571a7a1Sriastradh 			case DRM_ATI_GART_PCIE:
1841571a7a1Sriastradh 				val = (page_base >> 8) | 0xc;
1851571a7a1Sriastradh 				break;
1861571a7a1Sriastradh 			default:
1871571a7a1Sriastradh 			case DRM_ATI_GART_PCI:
1881571a7a1Sriastradh 				val = page_base;
1891571a7a1Sriastradh 				break;
1901571a7a1Sriastradh 			}
1911571a7a1Sriastradh 			if (gart_info->gart_table_location ==
1921571a7a1Sriastradh 			    DRM_ATI_GART_MAIN) {
1931571a7a1Sriastradh 				pci_gart[gart_idx] = cpu_to_le32(val);
1941571a7a1Sriastradh 			} else {
1951571a7a1Sriastradh 				offset = gart_idx * sizeof(u32);
1961571a7a1Sriastradh 				writel(val, (void __iomem *)map->handle + offset);
1971571a7a1Sriastradh 			}
1981571a7a1Sriastradh 			gart_idx++;
1991571a7a1Sriastradh 			page_base += ATI_PCIGART_PAGE_SIZE;
2001571a7a1Sriastradh 		}
2011571a7a1Sriastradh 	}
2021571a7a1Sriastradh 	ret = 0;
2031571a7a1Sriastradh 
2041571a7a1Sriastradh #if defined(__i386__) || defined(__x86_64__)
2051571a7a1Sriastradh 	wbinvd();
2061571a7a1Sriastradh #else
2071571a7a1Sriastradh 	mb();
2081571a7a1Sriastradh #endif
2091571a7a1Sriastradh 
2101571a7a1Sriastradh       done:
2111571a7a1Sriastradh 	gart_info->addr = address;
2121571a7a1Sriastradh 	gart_info->bus_addr = bus_address;
2131571a7a1Sriastradh 	return ret;
2141571a7a1Sriastradh }
215